From 25574e58b68dae94f7d9931b5e648a327a94ecd1 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 9 Jan 2018 11:39:23 -0800 Subject: Make core::ops::Place an unsafe trait --- src/liballoc/binary_heap.rs | 2 +- src/liballoc/boxed.rs | 2 +- src/liballoc/linked_list.rs | 4 ++-- src/liballoc/vec.rs | 2 +- src/liballoc/vec_deque.rs | 4 ++-- 5 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index 94bbaf92ce9..3041f85cd4c 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -1211,7 +1211,7 @@ where T: Clone + Ord { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for BinaryHeapPlace<'a, T> +unsafe impl<'a, T> Place for BinaryHeapPlace<'a, T> where T: Clone + Ord { fn pointer(&mut self) -> *mut T { self.place.pointer() diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6f125cdba81..c8ab3f681f8 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -142,7 +142,7 @@ pub struct IntermediateBox { #[unstable(feature = "placement_in", reason = "placement box design is still being worked out.", issue = "27779")] -impl Place for IntermediateBox { +unsafe impl Place for IntermediateBox { fn pointer(&mut self) -> *mut T { self.ptr as *mut T } diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 3ac5a85d721..ccb2da46f8d 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -1286,7 +1286,7 @@ impl<'a, T> Placer for FrontPlace<'a, T> { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for FrontPlace<'a, T> { +unsafe impl<'a, T> Place for FrontPlace<'a, T> { fn pointer(&mut self) -> *mut T { unsafe { &mut (*self.node.pointer()).element } } @@ -1341,7 +1341,7 @@ impl<'a, T> Placer for BackPlace<'a, T> { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for BackPlace<'a, T> { +unsafe impl<'a, T> Place for BackPlace<'a, T> { fn pointer(&mut self) -> *mut T { unsafe { &mut (*self.node.pointer()).element } } diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 301e44632b8..4a8982bf85c 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2544,7 +2544,7 @@ impl<'a, T> Placer for PlaceBack<'a, T> { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for PlaceBack<'a, T> { +unsafe impl<'a, T> Place for PlaceBack<'a, T> { fn pointer(&mut self) -> *mut T { unsafe { self.vec.as_mut_ptr().offset(self.vec.len as isize) } } diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index f56aa23a4eb..df49c1df082 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -2564,7 +2564,7 @@ impl<'a, T> Placer for PlaceBack<'a, T> { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for PlaceBack<'a, T> { +unsafe impl<'a, T> Place for PlaceBack<'a, T> { fn pointer(&mut self) -> *mut T { unsafe { self.vec_deque.ptr().offset(self.vec_deque.head as isize) } } @@ -2610,7 +2610,7 @@ impl<'a, T> Placer for PlaceFront<'a, T> { #[unstable(feature = "collection_placement", reason = "placement protocol is subject to change", issue = "30172")] -impl<'a, T> Place for PlaceFront<'a, T> { +unsafe impl<'a, T> Place for PlaceFront<'a, T> { fn pointer(&mut self) -> *mut T { let tail = self.vec_deque.wrap_sub(self.vec_deque.tail, 1); unsafe { self.vec_deque.ptr().offset(tail as isize) } -- cgit 1.4.1-3-g733a5 From 14982db2d68268458a3de03e395b2e9afe518b50 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 23 Dec 2017 19:28:33 -0800 Subject: in which the unused-parens lint comes to cover function and method args Resolves #46137. --- src/liballoc/vec_deque.rs | 2 +- src/librustc_apfloat/ieee.rs | 2 +- src/librustc_lint/unused.rs | 12 ++++++++++++ src/librustc_mir/dataflow/impls/borrows.rs | 2 +- src/librustc_resolve/lib.rs | 2 +- src/libstd/sys/unix/thread.rs | 4 ++-- src/test/compile-fail/lint-unnecessary-parens.rs | 12 +++++++----- 7 files changed, 25 insertions(+), 11 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index f56aa23a4eb..1f6c6660d9b 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -2480,7 +2480,7 @@ impl From> for Vec { if other.is_contiguous() { ptr::copy(buf.offset(tail as isize), buf, len); } else { - if (tail - head) >= cmp::min((cap - tail), head) { + if (tail - head) >= cmp::min(cap - tail, head) { // There is enough free space in the centre for the shortest block so we can // do this in at most three copy moves. if (cap - tail) > head { diff --git a/src/librustc_apfloat/ieee.rs b/src/librustc_apfloat/ieee.rs index 3e76b60b84a..7abd02b6656 100644 --- a/src/librustc_apfloat/ieee.rs +++ b/src/librustc_apfloat/ieee.rs @@ -1434,7 +1434,7 @@ impl Float for IeeeFloat { let max_change = S::MAX_EXP as i32 - (S::MIN_EXP as i32 - sig_bits) + 1; // Clamp to one past the range ends to let normalize handle overflow. - let exp_change = cmp::min(cmp::max(exp as i32, (-max_change - 1)), max_change); + let exp_change = cmp::min(cmp::max(exp as i32, -max_change - 1), max_change); self.exp = self.exp.saturating_add(exp_change as ExpInt); self = self.normalize(round, Loss::ExactlyZero).value; if self.is_nan() { diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 4e066ecf999..ef6475f9ee4 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -302,6 +302,18 @@ impl EarlyLintPass for UnusedParens { Assign(_, ref value) => (value, "assigned value", false), AssignOp(.., ref value) => (value, "assigned value", false), InPlace(_, ref value) => (value, "emplacement value", false), + Call(_, ref args) => { + for arg in args { + self.check_unused_parens_core(cx, arg, "function argument", false) + } + return; + }, + MethodCall(_, ref args) => { + for arg in &args[1..] { // first "argument" is self (which sometimes needs parens) + self.check_unused_parens_core(cx, arg, "method argument", false) + } + return; + } _ => return, }; self.check_unused_parens_core(cx, &value, msg, struct_lit_needs_parens); diff --git a/src/librustc_mir/dataflow/impls/borrows.rs b/src/librustc_mir/dataflow/impls/borrows.rs index f76aea19677..f543a33b130 100644 --- a/src/librustc_mir/dataflow/impls/borrows.rs +++ b/src/librustc_mir/dataflow/impls/borrows.rs @@ -131,7 +131,7 @@ impl<'tcx> fmt::Display for BorrowData<'tcx> { } impl ReserveOrActivateIndex { - fn reserved(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new((i.index() * 2)) } + fn reserved(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new(i.index() * 2) } fn active(i: BorrowIndex) -> Self { ReserveOrActivateIndex::new((i.index() * 2) + 1) } pub(crate) fn is_reservation(self) -> bool { self.index() % 2 == 0 } diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 5b9b3767cb6..231a32c4382 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -963,7 +963,7 @@ impl<'a> ModuleData<'a> { unresolved_invocations: RefCell::new(FxHashSet()), no_implicit_prelude: false, glob_importers: RefCell::new(Vec::new()), - globs: RefCell::new((Vec::new())), + globs: RefCell::new(Vec::new()), traits: RefCell::new(None), populated: Cell::new(normal_ancestor_id.is_local()), span, diff --git a/src/libstd/sys/unix/thread.rs b/src/libstd/sys/unix/thread.rs index cb249af4254..525882c1e1e 100644 --- a/src/libstd/sys/unix/thread.rs +++ b/src/libstd/sys/unix/thread.rs @@ -311,8 +311,8 @@ pub mod guard { #[cfg(target_os = "macos")] pub unsafe fn current() -> Option { - Some((libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize - - libc::pthread_get_stacksize_np(libc::pthread_self()))) + Some(libc::pthread_get_stackaddr_np(libc::pthread_self()) as usize - + libc::pthread_get_stacksize_np(libc::pthread_self())) } #[cfg(any(target_os = "openbsd", target_os = "bitrig"))] diff --git a/src/test/compile-fail/lint-unnecessary-parens.rs b/src/test/compile-fail/lint-unnecessary-parens.rs index b5eac73a55d..7cd0a6bbf0f 100644 --- a/src/test/compile-fail/lint-unnecessary-parens.rs +++ b/src/test/compile-fail/lint-unnecessary-parens.rs @@ -13,19 +13,19 @@ #[derive(Eq, PartialEq)] struct X { y: bool } impl X { - fn foo(&self) -> bool { self.y } + fn foo(&self, conjunct: bool) -> bool { self.y && conjunct } } fn foo() -> isize { return (1); //~ ERROR unnecessary parentheses around `return` value } -fn bar() -> X { - return (X { y: true }); //~ ERROR unnecessary parentheses around `return` value +fn bar(y: bool) -> X { + return (X { y }); //~ ERROR unnecessary parentheses around `return` value } fn main() { foo(); - bar(); + bar((true)); //~ ERROR unnecessary parentheses around function argument if (true) {} //~ ERROR unnecessary parentheses around `if` condition while (true) {} //~ ERROR unnecessary parentheses around `while` condition @@ -40,13 +40,15 @@ fn main() { if (X { y: true } == v) {} if (X { y: false }.y) {} - while (X { y: false }.foo()) {} + while (X { y: false }.foo(true)) {} while (true | X { y: false }.y) {} match (X { y: false }) { _ => {} } + X { y: false }.foo((true)); //~ ERROR unnecessary parentheses around method argument + let mut _a = (0); //~ ERROR unnecessary parentheses around assigned value _a = (0); //~ ERROR unnecessary parentheses around assigned value _a += (1); //~ ERROR unnecessary parentheses around assigned value -- cgit 1.4.1-3-g733a5 From 81e6840ff445f5eb9c99ba506aa89b8fc2a1a4c7 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Fri, 19 Jan 2018 16:51:46 +0100 Subject: Small improvements to the documentation of VecDeque. --- src/liballoc/vec_deque.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index f56aa23a4eb..7d15b790b24 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -906,7 +906,7 @@ impl VecDeque { } } - /// Clears the buffer, removing all values. + /// Clears the `VecDeque`, removing all values. /// /// # Examples /// @@ -1624,10 +1624,10 @@ impl VecDeque { return elem; } - /// Splits the collection into two at the given index. + /// Splits the `VecDeque` into two at the given index. /// - /// Returns a newly allocated `Self`. `self` contains elements `[0, at)`, - /// and the returned `Self` contains elements `[at, len)`. + /// Returns a newly allocated `VecDeque`. `self` contains elements `[0, at)`, + /// and the returned `VecDeque` contains elements `[at, len)`. /// /// Note that the capacity of `self` does not change. /// @@ -1635,7 +1635,7 @@ impl VecDeque { /// /// # Panics /// - /// Panics if `at > len` + /// Panics if `at` is out of bounds. /// /// # Examples /// @@ -1815,7 +1815,8 @@ impl VecDeque { impl VecDeque { /// Modifies the `VecDeque` in-place so that `len()` is equal to new_len, - /// either by removing excess elements or by appending clones of `value` to the back. + /// either by removing excess elements from the back or by appending clones of `value` + /// to the back. /// /// # Examples /// @@ -2390,7 +2391,7 @@ impl IntoIterator for VecDeque { type Item = T; type IntoIter = IntoIter; - /// Consumes the list into a front-to-back iterator yielding elements by + /// Consumes the `VecDeque` into a front-to-back iterator yielding elements by /// value. fn into_iter(self) -> IntoIter { IntoIter { inner: self } -- cgit 1.4.1-3-g733a5 From f19baf0977b176ba26277af479a19b71b7ee1fdb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 18:58:39 +0100 Subject: Rename std::ptr::Shared to NonNull `Shared` is now a deprecated `type` alias. CC https://github.com/rust-lang/rust/issues/27730#issuecomment-352800629 --- src/liballoc/arc.rs | 18 +++---- src/liballoc/heap.rs | 2 +- src/liballoc/lib.rs | 2 +- src/liballoc/linked_list.rs | 28 +++++----- src/liballoc/rc.rs | 20 +++---- src/liballoc/vec.rs | 10 ++-- src/liballoc/vec_deque.rs | 6 +-- src/libcore/ptr.rs | 87 ++++++++++++++++--------------- src/librustc_data_structures/array_vec.rs | 6 +-- src/librustc_data_structures/lib.rs | 2 +- src/libstd/collections/hash/table.rs | 6 +-- src/libstd/lib.rs | 2 +- src/libstd/panic.rs | 6 +-- 13 files changed, 100 insertions(+), 95 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 185af8835d1..49a6220d591 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -25,7 +25,7 @@ use core::intrinsics::abort; use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; -use core::ptr::{self, Shared}; +use core::ptr::{self, NonNull}; use core::marker::{Unsize, PhantomData}; use core::hash::{Hash, Hasher}; use core::{isize, usize}; @@ -197,7 +197,7 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize; /// [rc_examples]: ../../std/rc/index.html#examples #[stable(feature = "rust1", since = "1.0.0")] pub struct Arc { - ptr: Shared>, + ptr: NonNull>, phantom: PhantomData, } @@ -234,7 +234,7 @@ impl, U: ?Sized> CoerceUnsized> for Arc {} /// [`None`]: ../../std/option/enum.Option.html#variant.None #[stable(feature = "arc_weak", since = "1.4.0")] pub struct Weak { - ptr: Shared>, + ptr: NonNull>, } #[stable(feature = "arc_weak", since = "1.4.0")] @@ -286,7 +286,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }; - Arc { ptr: Shared::from(Box::into_unique(x)), phantom: PhantomData } + Arc { ptr: NonNull::from(Box::into_unique(x)), phantom: PhantomData } } /// Returns the contained value, if the `Arc` has exactly one strong reference. @@ -397,7 +397,7 @@ impl Arc { let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); Arc { - ptr: Shared::new_unchecked(arc_ptr), + ptr: NonNull::new_unchecked(arc_ptr), phantom: PhantomData, } } @@ -582,7 +582,7 @@ impl Arc { // Free the allocation without dropping its contents box_free(bptr); - Arc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } } @@ -609,7 +609,7 @@ impl Arc<[T]> { &mut (*ptr).data as *mut [T] as *mut T, v.len()); - Arc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } @@ -669,7 +669,7 @@ impl ArcFromSlice for Arc<[T]> { // All clear. Forget the guard so it doesn't free the new ArcInner. mem::forget(guard); - Arc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } } @@ -991,7 +991,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Shared::from(Box::into_unique(box ArcInner { + ptr: NonNull::from(Box::into_unique(box ArcInner { strong: atomic::AtomicUsize::new(0), weak: atomic::AtomicUsize::new(1), data: uninitialized(), diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index b2bd9d7d8fa..37af9ea5295 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -232,7 +232,7 @@ unsafe impl Alloc for Heap { /// /// This preserves the non-null invariant for types like `Box`. The address /// may overlap with non-zero-size memory allocations. -#[rustc_deprecated(since = "1.19", reason = "Use Unique/Shared::empty() instead")] +#[rustc_deprecated(since = "1.19", reason = "Use Unique/NonNull::empty() instead")] #[unstable(feature = "heap_api", issue = "27700")] pub const EMPTY: *mut () = 1 as *mut (); diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 6ee4f802802..eaad6f1116f 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -103,6 +103,7 @@ #![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] +#![feature(nonnull)] #![feature(nonzero)] #![feature(offset_to)] #![feature(optin_builtin_traits)] @@ -110,7 +111,6 @@ #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(rustc_attrs)] -#![feature(shared)] #![feature(slice_get_slice)] #![feature(slice_patterns)] #![feature(slice_rsplit)] diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 3ac5a85d721..e6e84101275 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -29,7 +29,7 @@ use core::iter::{FromIterator, FusedIterator}; use core::marker::PhantomData; use core::mem; use core::ops::{BoxPlace, InPlace, Place, Placer}; -use core::ptr::{self, Shared}; +use core::ptr::{self, NonNull}; use boxed::{Box, IntermediateBox}; use super::SpecExtend; @@ -44,15 +44,15 @@ use super::SpecExtend; /// more memory efficient and make better use of CPU cache. #[stable(feature = "rust1", since = "1.0.0")] pub struct LinkedList { - head: Option>>, - tail: Option>>, + head: Option>>, + tail: Option>>, len: usize, marker: PhantomData>>, } struct Node { - next: Option>>, - prev: Option>>, + next: Option>>, + prev: Option>>, element: T, } @@ -65,8 +65,8 @@ struct Node { /// [`LinkedList`]: struct.LinkedList.html #[stable(feature = "rust1", since = "1.0.0")] pub struct Iter<'a, T: 'a> { - head: Option>>, - tail: Option>>, + head: Option>>, + tail: Option>>, len: usize, marker: PhantomData<&'a Node>, } @@ -98,8 +98,8 @@ impl<'a, T> Clone for Iter<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] pub struct IterMut<'a, T: 'a> { list: &'a mut LinkedList, - head: Option>>, - tail: Option>>, + head: Option>>, + tail: Option>>, len: usize, } @@ -157,7 +157,7 @@ impl LinkedList { unsafe { node.next = self.head; node.prev = None; - let node = Some(Shared::from(Box::into_unique(node))); + let node = Some(NonNull::from(Box::into_unique(node))); match self.head { None => self.tail = node, @@ -192,7 +192,7 @@ impl LinkedList { unsafe { node.next = None; node.prev = self.tail; - let node = Some(Shared::from(Box::into_unique(node))); + let node = Some(NonNull::from(Box::into_unique(node))); match self.tail { None => self.head = node, @@ -225,7 +225,7 @@ impl LinkedList { /// /// Warning: this will not check that the provided node belongs to the current list. #[inline] - unsafe fn unlink_node(&mut self, mut node: Shared>) { + unsafe fn unlink_node(&mut self, mut node: NonNull>) { let node = node.as_mut(); match node.prev { @@ -986,7 +986,7 @@ impl<'a, T> IterMut<'a, T> { Some(prev) => prev, }; - let node = Some(Shared::from(Box::into_unique(box Node { + let node = Some(NonNull::from(Box::into_unique(box Node { next: Some(head), prev: Some(prev), element, @@ -1038,7 +1038,7 @@ pub struct DrainFilter<'a, T: 'a, F: 'a> where F: FnMut(&mut T) -> bool, { list: &'a mut LinkedList, - it: Option>>, + it: Option>>, pred: F, idx: usize, old_len: usize, diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 59079f9ba76..aa7b96139fa 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -256,7 +256,7 @@ use core::marker::{Unsize, PhantomData}; use core::mem::{self, align_of_val, forget, size_of_val, uninitialized}; use core::ops::Deref; use core::ops::CoerceUnsized; -use core::ptr::{self, Shared}; +use core::ptr::{self, NonNull}; use core::convert::From; use heap::{Heap, Alloc, Layout, box_free}; @@ -282,7 +282,7 @@ struct RcBox { /// [get_mut]: #method.get_mut #[stable(feature = "rust1", since = "1.0.0")] pub struct Rc { - ptr: Shared>, + ptr: NonNull>, phantom: PhantomData, } @@ -311,7 +311,7 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - ptr: Shared::from(Box::into_unique(box RcBox { + ptr: NonNull::from(Box::into_unique(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value, @@ -428,7 +428,7 @@ impl Rc { let rc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); Rc { - ptr: Shared::new_unchecked(rc_ptr), + ptr: NonNull::new_unchecked(rc_ptr), phantom: PhantomData, } } @@ -649,7 +649,7 @@ impl Rc { let raw: *const RcBox = self.ptr.as_ptr(); forget(self); Ok(Rc { - ptr: Shared::new_unchecked(raw as *const RcBox as *mut _), + ptr: NonNull::new_unchecked(raw as *const RcBox as *mut _), phantom: PhantomData, }) } @@ -695,7 +695,7 @@ impl Rc { // Free the allocation without dropping its contents box_free(bptr); - Rc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Rc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } } @@ -722,7 +722,7 @@ impl Rc<[T]> { &mut (*ptr).value as *mut [T] as *mut T, v.len()); - Rc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Rc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } @@ -781,7 +781,7 @@ impl RcFromSlice for Rc<[T]> { // All clear. Forget the guard so it doesn't free the new RcBox. forget(guard); - Rc { ptr: Shared::new_unchecked(ptr), phantom: PhantomData } + Rc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } } } @@ -1160,7 +1160,7 @@ impl From> for Rc<[T]> { /// [`None`]: ../../std/option/enum.Option.html#variant.None #[stable(feature = "rc_weak", since = "1.4.0")] pub struct Weak { - ptr: Shared>, + ptr: NonNull>, } #[stable(feature = "rc_weak", since = "1.4.0")] @@ -1190,7 +1190,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Shared::from(Box::into_unique(box RcBox { + ptr: NonNull::from(Box::into_unique(box RcBox { strong: Cell::new(0), weak: Cell::new(1), value: uninitialized(), diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 301e44632b8..b14b9d74765 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -78,7 +78,7 @@ use core::num::Float; use core::ops::{InPlace, Index, IndexMut, Place, Placer}; use core::ops; use core::ptr; -use core::ptr::Shared; +use core::ptr::NonNull; use core::slice; use borrow::ToOwned; @@ -1124,7 +1124,7 @@ impl Vec { tail_start: end, tail_len: len - end, iter: range_slice.iter(), - vec: Shared::from(self), + vec: NonNull::from(self), } } } @@ -1745,7 +1745,7 @@ impl IntoIterator for Vec { let cap = self.buf.cap(); mem::forget(self); IntoIter { - buf: Shared::new_unchecked(begin), + buf: NonNull::new_unchecked(begin), phantom: PhantomData, cap, ptr: begin, @@ -2267,7 +2267,7 @@ impl<'a, T> FromIterator for Cow<'a, [T]> where T: Clone { /// [`IntoIterator`]: ../../std/iter/trait.IntoIterator.html #[stable(feature = "rust1", since = "1.0.0")] pub struct IntoIter { - buf: Shared, + buf: NonNull, phantom: PhantomData, cap: usize, ptr: *const T, @@ -2442,7 +2442,7 @@ pub struct Drain<'a, T: 'a> { tail_len: usize, /// Current remaining range to remove iter: slice::Iter<'a, T>, - vec: Shared>, + vec: NonNull>, } #[stable(feature = "collection_debug", since = "1.17.0")] diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index f56aa23a4eb..8f05a69c5f3 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -23,7 +23,7 @@ use core::iter::{repeat, FromIterator, FusedIterator}; use core::mem; use core::ops::{Index, IndexMut, Place, Placer, InPlace}; use core::ptr; -use core::ptr::Shared; +use core::ptr::NonNull; use core::slice; use core::hash::{Hash, Hasher}; @@ -895,7 +895,7 @@ impl VecDeque { self.head = drain_tail; Drain { - deque: Shared::from(&mut *self), + deque: NonNull::from(&mut *self), after_tail: drain_head, after_head: head, iter: Iter { @@ -2154,7 +2154,7 @@ pub struct Drain<'a, T: 'a> { after_tail: usize, after_head: usize, iter: Iter<'a, T>, - deque: Shared>, + deque: NonNull>, } #[stable(feature = "collection_debug", since = "1.17.0")] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 4da51a33128..fd8f9138f36 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2321,7 +2321,7 @@ impl PartialOrd for *mut T { /// its owning Unique. /// /// If you're uncertain of whether it's correct to use `Unique` for your purposes, -/// consider using `Shared`, which has weaker semantics. +/// consider using `NonNull`, which has weaker semantics. /// /// Unlike `*mut T`, the pointer must always be non-null, even if the pointer /// is never dereferenced. This is so that enums may use this forbidden value @@ -2452,18 +2452,23 @@ impl<'a, T: ?Sized> From<&'a T> for Unique { } } +/// Previous name of `NonNull`. +#[rustc_deprecated(since = "1.24", reason = "renamed to `NonNull`")] +#[unstable(feature = "shared", issue = "27730")] +pub type Shared = NonNull; + /// `*mut T` but non-zero and covariant. /// /// This is often the correct thing to use when building data structures using /// raw pointers, but is ultimately more dangerous to use because of its additional -/// properties. If you're not sure if you should use `Shared`, just use `*mut T`! +/// properties. If you're not sure if you should use `NonNull`, just use `*mut T`! /// /// Unlike `*mut T`, the pointer must always be non-null, even if the pointer /// is never dereferenced. This is so that enums may use this forbidden value -/// as a discriminant -- `Option>` has the same size as `Shared`. +/// as a discriminant -- `Option>` has the same size as `NonNull`. /// However the pointer may still dangle if it isn't dereferenced. /// -/// Unlike `*mut T`, `Shared` is covariant over `T`. If this is incorrect +/// Unlike `*mut T`, `NonNull` is covariant over `T`. If this is incorrect /// for your use case, you should include some PhantomData in your type to /// provide invariance, such as `PhantomData>` or `PhantomData<&'a mut T>`. /// Usually this won't be necessary; covariance is correct for most safe abstractions, @@ -2471,56 +2476,56 @@ impl<'a, T: ?Sized> From<&'a T> for Unique { /// provide a public API that follows the normal shared XOR mutable rules of Rust. #[unstable(feature = "shared", reason = "needs an RFC to flesh out design", issue = "27730")] -pub struct Shared { +pub struct NonNull { pointer: NonZero<*const T>, } #[unstable(feature = "shared", issue = "27730")] -impl fmt::Debug for Shared { +impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:p}", self.as_ptr()) } } -/// `Shared` pointers are not `Send` because the data they reference may be aliased. +/// `NonNull` pointers are not `Send` because the data they reference may be aliased. // NB: This impl is unnecessary, but should provide better error messages. -#[unstable(feature = "shared", issue = "27730")] -impl !Send for Shared { } +#[unstable(feature = "nonnull", issue = "27730")] +impl !Send for NonNull { } -/// `Shared` pointers are not `Sync` because the data they reference may be aliased. +/// `NonNull` pointers are not `Sync` because the data they reference may be aliased. // NB: This impl is unnecessary, but should provide better error messages. -#[unstable(feature = "shared", issue = "27730")] -impl !Sync for Shared { } +#[unstable(feature = "nonnull", issue = "27730")] +impl !Sync for NonNull { } -#[unstable(feature = "shared", issue = "27730")] -impl Shared { - /// Creates a new `Shared` that is dangling, but well-aligned. +#[unstable(feature = "nonnull", issue = "27730")] +impl NonNull { + /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like /// `Vec::new` does. pub fn empty() -> Self { unsafe { let ptr = mem::align_of::() as *mut T; - Shared::new_unchecked(ptr) + NonNull::new_unchecked(ptr) } } } -#[unstable(feature = "shared", issue = "27730")] -impl Shared { - /// Creates a new `Shared`. +#[unstable(feature = "nonnull", issue = "27730")] +impl NonNull { + /// Creates a new `NonNull`. /// /// # Safety /// /// `ptr` must be non-null. - #[unstable(feature = "shared", issue = "27730")] + #[unstable(feature = "nonnull", issue = "27730")] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { - Shared { pointer: NonZero::new_unchecked(ptr) } + NonNull { pointer: NonZero::new_unchecked(ptr) } } - /// Creates a new `Shared` if `ptr` is non-null. + /// Creates a new `NonNull` if `ptr` is non-null. pub fn new(ptr: *mut T) -> Option { - NonZero::new(ptr as *const T).map(|nz| Shared { pointer: nz }) + NonZero::new(ptr as *const T).map(|nz| NonNull { pointer: nz }) } /// Acquires the underlying `*mut` pointer. @@ -2548,49 +2553,49 @@ impl Shared { /// Acquires the underlying pointer as a `*mut` pointer. #[rustc_deprecated(since = "1.19", reason = "renamed to `as_ptr` for ergonomics/consistency")] - #[unstable(feature = "shared", issue = "27730")] + #[unstable(feature = "nonnull", issue = "27730")] pub unsafe fn as_mut_ptr(&self) -> *mut T { self.as_ptr() } } -#[unstable(feature = "shared", issue = "27730")] -impl Clone for Shared { +#[unstable(feature = "nonnull", issue = "27730")] +impl Clone for NonNull { fn clone(&self) -> Self { *self } } -#[unstable(feature = "shared", issue = "27730")] -impl Copy for Shared { } +#[unstable(feature = "nonnull", issue = "27730")] +impl Copy for NonNull { } -#[unstable(feature = "shared", issue = "27730")] -impl CoerceUnsized> for Shared where T: Unsize { } +#[unstable(feature = "nonnull", issue = "27730")] +impl CoerceUnsized> for NonNull where T: Unsize { } -#[unstable(feature = "shared", issue = "27730")] -impl fmt::Pointer for Shared { +#[unstable(feature = "nonnull", issue = "27730")] +impl fmt::Pointer for NonNull { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&self.as_ptr(), f) } } -#[unstable(feature = "shared", issue = "27730")] -impl From> for Shared { +#[unstable(feature = "nonnull", issue = "27730")] +impl From> for NonNull { fn from(unique: Unique) -> Self { - Shared { pointer: unique.pointer } + NonNull { pointer: unique.pointer } } } -#[unstable(feature = "shared", issue = "27730")] -impl<'a, T: ?Sized> From<&'a mut T> for Shared { +#[unstable(feature = "nonnull", issue = "27730")] +impl<'a, T: ?Sized> From<&'a mut T> for NonNull { fn from(reference: &'a mut T) -> Self { - Shared { pointer: NonZero::from(reference) } + NonNull { pointer: NonZero::from(reference) } } } -#[unstable(feature = "shared", issue = "27730")] -impl<'a, T: ?Sized> From<&'a T> for Shared { +#[unstable(feature = "nonnull", issue = "27730")] +impl<'a, T: ?Sized> From<&'a T> for NonNull { fn from(reference: &'a T) -> Self { - Shared { pointer: NonZero::from(reference) } + NonNull { pointer: NonZero::from(reference) } } } diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 57fc78ef531..511c407d45a 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -12,7 +12,7 @@ use std::marker::Unsize; use std::iter::Extend; -use std::ptr::{self, drop_in_place, Shared}; +use std::ptr::{self, drop_in_place, NonNull}; use std::ops::{Deref, DerefMut, Range}; use std::hash::{Hash, Hasher}; use std::slice; @@ -146,7 +146,7 @@ impl ArrayVec { tail_start: end, tail_len: len - end, iter: range_slice.iter(), - array_vec: Shared::from(self), + array_vec: NonNull::from(self), } } } @@ -232,7 +232,7 @@ pub struct Drain<'a, A: Array> tail_start: usize, tail_len: usize, iter: slice::Iter<'a, ManuallyDrop>, - array_vec: Shared>, + array_vec: NonNull>, } impl<'a, A: Array> Iterator for Drain<'a, A> { diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 24048e606df..1d53825ac37 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -21,8 +21,8 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] -#![feature(shared)] #![feature(collections_range)] +#![feature(nonnull)] #![feature(nonzero)] #![feature(unboxed_closures)] #![feature(fn_traits)] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 96f98efe4aa..73bd5747c10 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -16,7 +16,7 @@ use marker; use mem::{align_of, size_of, needs_drop}; use mem; use ops::{Deref, DerefMut}; -use ptr::{self, Unique, Shared}; +use ptr::{self, Unique, NonNull}; use self::BucketState::*; @@ -873,7 +873,7 @@ impl RawTable { elems_left, marker: marker::PhantomData, }, - table: Shared::from(self), + table: NonNull::from(self), marker: marker::PhantomData, } } @@ -1020,7 +1020,7 @@ impl IntoIter { /// Iterator over the entries in a table, clearing the table. pub struct Drain<'a, K: 'a, V: 'a> { - table: Shared>, + table: NonNull>, iter: RawBuckets<'static, K, V>, marker: marker::PhantomData<&'a RawTable>, } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index bb38fc55091..8a1ba32f7dc 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -283,6 +283,7 @@ #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] #![feature(never_type)] +#![feature(nonnull)] #![feature(num_bits_bytes)] #![feature(old_wrapping)] #![feature(on_unimplemented)] @@ -297,7 +298,6 @@ #![feature(raw)] #![feature(repr_align)] #![feature(rustc_attrs)] -#![feature(shared)] #![feature(sip_hash_13)] #![feature(slice_bytes)] #![feature(slice_concat_ext)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 53c2211745c..68584b7cf25 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -17,7 +17,7 @@ use cell::UnsafeCell; use fmt; use ops::{Deref, DerefMut}; use panicking; -use ptr::{Unique, Shared}; +use ptr::{Unique, NonNull}; use rc::Rc; use sync::{Arc, Mutex, RwLock, atomic}; use thread::Result; @@ -198,8 +198,8 @@ impl UnwindSafe for *const T {} impl UnwindSafe for *mut T {} #[unstable(feature = "unique", issue = "27730")] impl UnwindSafe for Unique {} -#[unstable(feature = "shared", issue = "27730")] -impl UnwindSafe for Shared {} +#[unstable(feature = "nonnull", issue = "27730")] +impl UnwindSafe for NonNull {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl UnwindSafe for Mutex {} #[stable(feature = "catch_unwind", since = "1.9.0")] -- cgit 1.4.1-3-g733a5 From fb03a49c2501c52401b3c987fd455818de1736f2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 19:12:22 +0100 Subject: Replace Unique with NonZero in Alloc trait --- src/liballoc/allocator.rs | 20 ++++++++++---------- src/liballoc/raw_vec.rs | 2 +- src/libcore/ptr.rs | 7 +++++++ src/test/run-pass/allocator-alloc-one.rs | 2 +- 4 files changed, 19 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index c2a8f5f8ff9..55e8c0b430f 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -19,7 +19,7 @@ use core::cmp; use core::fmt; use core::mem; use core::usize; -use core::ptr::{self, Unique}; +use core::ptr::{self, NonNull}; /// Represents the combination of a starting address and /// a total capacity of the returned block. @@ -895,12 +895,12 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - fn alloc_one(&mut self) -> Result, AllocErr> + fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { let k = Layout::new::(); if k.size() > 0 { - unsafe { self.alloc(k).map(|p| Unique::new_unchecked(p as *mut T)) } + unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } } else { Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) } @@ -923,7 +923,7 @@ pub unsafe trait Alloc { /// * `ptr` must denote a block of memory currently allocated via this allocator /// /// * the layout of `T` must *fit* that block of memory. - unsafe fn dealloc_one(&mut self, ptr: Unique) + unsafe fn dealloc_one(&mut self, ptr: NonNull) where Self: Sized { let raw_ptr = ptr.as_ptr() as *mut u8; @@ -963,7 +963,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - fn alloc_array(&mut self, n: usize) -> Result, AllocErr> + fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { match Layout::array::(n) { @@ -971,7 +971,7 @@ pub unsafe trait Alloc { unsafe { self.alloc(layout.clone()) .map(|p| { - Unique::new_unchecked(p as *mut T) + NonNull::new_unchecked(p as *mut T) }) } } @@ -1012,15 +1012,15 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_array(&mut self, - ptr: Unique, + ptr: NonNull, n_old: usize, - n_new: usize) -> Result, AllocErr> + n_new: usize) -> Result, AllocErr> where Self: Sized { match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) - .map(|p|Unique::new_unchecked(p as *mut T)) + .map(|p| NonNull::new_unchecked(p as *mut T)) } _ => { Err(AllocErr::invalid_input("invalid layout for realloc_array")) @@ -1048,7 +1048,7 @@ pub unsafe trait Alloc { /// constraints. /// /// Always returns `Err` on arithmetic overflow. - unsafe fn dealloc_array(&mut self, ptr: Unique, n: usize) -> Result<(), AllocErr> + unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> where Self: Sized { let raw_ptr = ptr.as_ptr() as *mut u8; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index dbf1fb1367d..621e1906961 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -322,7 +322,7 @@ impl RawVec { // would cause overflow let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { - Ok(ptr) => (new_cap, ptr), + Ok(ptr) => (new_cap, ptr.into()), Err(e) => self.a.oom(e), } } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index fd8f9138f36..89ecb3457fc 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2452,6 +2452,13 @@ impl<'a, T: ?Sized> From<&'a T> for Unique { } } +#[unstable(feature = "unique", issue = "27730")] +impl<'a, T: ?Sized> From> for Unique { + fn from(p: NonNull) -> Self { + Unique { pointer: p.pointer, _marker: PhantomData } + } +} + /// Previous name of `NonNull`. #[rustc_deprecated(since = "1.24", reason = "renamed to `NonNull`")] #[unstable(feature = "shared", issue = "27730")] diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index 712fa2d6001..eaa5bc90805 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(allocator_api, unique)] +#![feature(allocator_api, nonnull)] use std::heap::{Heap, Alloc}; -- cgit 1.4.1-3-g733a5 From a2f878a084c8000dd1dcacc02cae5ebc5603fe72 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 19:24:07 +0100 Subject: Replace Box::{from,into}_unique with {from,into}_nonnull_raw MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Thew `_raw` prefix is included because the fact that `Box`’s ownership semantics are "dissolved" or recreated seem more important than the exact parameter type or return type. --- src/liballoc/arc.rs | 6 +++--- src/liballoc/boxed.rs | 47 +++++++++++++++++++++++++------------------ src/liballoc/linked_list.rs | 8 ++++---- src/liballoc/rc.rs | 8 ++++---- src/test/rustdoc-js/from_u.js | 1 - 5 files changed, 38 insertions(+), 32 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 49a6220d591..e490f67dd92 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -286,7 +286,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }; - Arc { ptr: NonNull::from(Box::into_unique(x)), phantom: PhantomData } + Arc { ptr: Box::into_nonnull_raw(x), phantom: PhantomData } } /// Returns the contained value, if the `Arc` has exactly one strong reference. @@ -991,11 +991,11 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: NonNull::from(Box::into_unique(box ArcInner { + ptr: Box::into_nonnull_raw(box ArcInner { strong: atomic::AtomicUsize::new(0), weak: atomic::AtomicUsize::new(1), data: uninitialized(), - })), + }), } } } diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6f125cdba81..994466e2249 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -68,7 +68,7 @@ use core::marker::{self, Unsize}; use core::mem; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer}; -use core::ptr::{self, Unique}; +use core::ptr::{self, NonNull, Unique}; use core::convert::From; use str::from_boxed_utf8_unchecked; @@ -269,38 +269,38 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub unsafe fn from_raw(raw: *mut T) -> Self { - Box::from_unique(Unique::new_unchecked(raw)) + Box(Unique::new_unchecked(raw)) } - /// Constructs a `Box` from a `Unique` pointer. + /// Constructs a `Box` from a `NonNull` pointer. /// /// After calling this function, the memory is owned by a `Box` and `T` can /// then be destroyed and released upon drop. /// /// # Safety /// - /// A `Unique` can be safely created via [`Unique::new`] and thus doesn't + /// A `NonNull` can be safely created via [`NonNull::new`] and thus doesn't /// necessarily own the data pointed to nor is the data guaranteed to live /// as long as the pointer. /// - /// [`Unique::new`]: ../../core/ptr/struct.Unique.html#method.new + /// [`NonNull::new`]: ../../core/ptr/struct.NonNull.html#method.new /// /// # Examples /// /// ``` - /// #![feature(unique)] + /// #![feature(nonnull)] /// /// fn main() { /// let x = Box::new(5); - /// let ptr = Box::into_unique(x); - /// let x = unsafe { Box::from_unique(ptr) }; + /// let ptr = Box::into_nonnull_raw(x); + /// let x = unsafe { Box::from_nonnull_raw(ptr) }; /// } /// ``` - #[unstable(feature = "unique", reason = "needs an RFC to flesh out design", + #[unstable(feature = "nonnull", reason = "needs an RFC to flesh out design", issue = "27730")] #[inline] - pub unsafe fn from_unique(u: Unique) -> Self { - Box(u) + pub unsafe fn from_nonnull_raw(u: NonNull) -> Self { + Box(u.into()) } /// Consumes the `Box`, returning the wrapped raw pointer. @@ -326,41 +326,48 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Box) -> *mut T { - Box::into_unique(b).as_ptr() + Box::into_nonnull_raw(b).as_ptr() } - /// Consumes the `Box`, returning the wrapped pointer as `Unique`. + /// Consumes the `Box`, returning the wrapped pointer as `NonNull`. /// /// After calling this function, the caller is responsible for the /// memory previously managed by the `Box`. In particular, the /// caller should properly destroy `T` and release the memory. The - /// proper way to do so is to either convert the `Unique` pointer: + /// proper way to do so is to either convert the `NonNull` pointer: /// - /// - Into a `Box` with the [`Box::from_unique`] function. + /// - Into a `Box` with the [`Box::from_nonnull_raw`] function. /// /// - Into a raw pointer and back into a `Box` with the [`Box::from_raw`] /// function. /// /// Note: this is an associated function, which means that you have - /// to call it as `Box::into_unique(b)` instead of `b.into_unique()`. This + /// to call it as `Box::into_nonnull_raw(b)` + /// instead of `b.into_nonnull_raw()`. This /// is so that there is no conflict with a method on the inner type. /// - /// [`Box::from_unique`]: struct.Box.html#method.from_unique + /// [`Box::from_nonnull_raw`]: struct.Box.html#method.from_nonnull_raw /// [`Box::from_raw`]: struct.Box.html#method.from_raw /// /// # Examples /// /// ``` - /// #![feature(unique)] + /// #![feature(nonnull)] /// /// fn main() { /// let x = Box::new(5); - /// let ptr = Box::into_unique(x); + /// let ptr = Box::into_nonnull_raw(x); /// } /// ``` - #[unstable(feature = "unique", reason = "needs an RFC to flesh out design", + #[unstable(feature = "nonnull", reason = "needs an RFC to flesh out design", issue = "27730")] #[inline] + pub fn into_nonnull_raw(b: Box) -> NonNull { + Box::into_unique(b).into() + } + + #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_nonnull_raw instead")] + #[inline] pub fn into_unique(b: Box) -> Unique { let unique = b.0; mem::forget(b); diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index e6e84101275..4c4a00e53fa 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -157,7 +157,7 @@ impl LinkedList { unsafe { node.next = self.head; node.prev = None; - let node = Some(NonNull::from(Box::into_unique(node))); + let node = Some(Box::into_nonnull_raw(node)); match self.head { None => self.tail = node, @@ -192,7 +192,7 @@ impl LinkedList { unsafe { node.next = None; node.prev = self.tail; - let node = Some(NonNull::from(Box::into_unique(node))); + let node = Some(Box::into_nonnull_raw(node)); match self.tail { None => self.head = node, @@ -986,11 +986,11 @@ impl<'a, T> IterMut<'a, T> { Some(prev) => prev, }; - let node = Some(NonNull::from(Box::into_unique(box Node { + let node = Some(Box::into_nonnull_raw(box Node { next: Some(head), prev: Some(prev), element, - }))); + })); prev.as_mut().next = node; head.as_mut().prev = node; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index aa7b96139fa..590a6837905 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -311,11 +311,11 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - ptr: NonNull::from(Box::into_unique(box RcBox { + ptr: Box::into_nonnull_raw(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value, - })), + }), phantom: PhantomData, } } @@ -1190,11 +1190,11 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: NonNull::from(Box::into_unique(box RcBox { + ptr: Box::into_nonnull_raw(box RcBox { strong: Cell::new(0), weak: Cell::new(1), value: uninitialized(), - })), + }), } } } diff --git a/src/test/rustdoc-js/from_u.js b/src/test/rustdoc-js/from_u.js index 920620a9aee..0296788f7a0 100644 --- a/src/test/rustdoc-js/from_u.js +++ b/src/test/rustdoc-js/from_u.js @@ -15,7 +15,6 @@ const EXPECTED = { { 'path': 'std::char', 'name': 'from_u32' }, { 'path': 'std::str', 'name': 'from_utf8' }, { 'path': 'std::string::String', 'name': 'from_utf8' }, - { 'path': 'std::boxed::Box', 'name': 'from_unique' }, { 'path': 'std::i32', 'name': 'from_unsigned' }, { 'path': 'std::i128', 'name': 'from_unsigned' }, ], -- cgit 1.4.1-3-g733a5 From c97c1f7dc3b8c2e5e1c40681094c45cf55be5832 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 19:29:16 +0100 Subject: Mark Unique as perma-unstable, with the feature renamed to ptr_internals. --- src/doc/nomicon | 2 +- src/liballoc/lib.rs | 2 +- src/libcore/ptr.rs | 30 +++++++++++++++--------------- src/libcore/tests/lib.rs | 2 +- src/libcore/tests/ptr.rs | 4 ++-- src/libstd/lib.rs | 2 +- src/libstd/panic.rs | 2 +- src/test/run-pass/issue-23433.rs | 6 +++--- 8 files changed, 25 insertions(+), 25 deletions(-) (limited to 'src/liballoc') diff --git a/src/doc/nomicon b/src/doc/nomicon index 2f7b05fd593..fec3182d0b0 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 2f7b05fd5939aa49d52c4ab309b9a47776ba7bd8 +Subproject commit fec3182d0b0a3cf8122e192b3270064a5b19be5b diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index eaad6f1116f..07e4ccc45a9 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -110,6 +110,7 @@ #![feature(pattern)] #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] +#![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] #![feature(slice_patterns)] @@ -120,7 +121,6 @@ #![feature(trusted_len)] #![feature(unboxed_closures)] #![feature(unicode)] -#![feature(unique)] #![feature(unsize)] #![feature(allocator_internals)] #![feature(on_unimplemented)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 89ecb3457fc..e39c520880a 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2330,8 +2330,9 @@ impl PartialOrd for *mut T { /// /// Unlike `*mut T`, `Unique` is covariant over `T`. This should always be correct /// for any type which upholds Unique's aliasing requirements. -#[unstable(feature = "unique", reason = "needs an RFC to flesh out design", - issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0", + reason = "use NonNull instead and consider PhantomData \ + (if you also use #[may_dangle]), Send, and/or Sync")] pub struct Unique { pointer: NonZero<*const T>, // NOTE: this marker has no consequences for variance, but is necessary @@ -2342,7 +2343,7 @@ pub struct Unique { _marker: PhantomData, } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl fmt::Debug for Unique { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:p}", self.as_ptr()) @@ -2353,17 +2354,17 @@ impl fmt::Debug for Unique { /// reference is unaliased. Note that this aliasing invariant is /// unenforced by the type system; the abstraction using the /// `Unique` must enforce it. -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] unsafe impl Send for Unique { } /// `Unique` pointers are `Sync` if `T` is `Sync` because the data they /// reference is unaliased. Note that this aliasing invariant is /// unenforced by the type system; the abstraction using the /// `Unique` must enforce it. -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] unsafe impl Sync for Unique { } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl Unique { /// Creates a new `Unique` that is dangling, but well-aligned. /// @@ -2377,14 +2378,13 @@ impl Unique { } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl Unique { /// Creates a new `Unique`. /// /// # Safety /// /// `ptr` must be non-null. - #[unstable(feature = "unique", issue = "27730")] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { Unique { pointer: NonZero::new_unchecked(ptr), _marker: PhantomData } } @@ -2418,41 +2418,41 @@ impl Unique { } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl Clone for Unique { fn clone(&self) -> Self { *self } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl Copy for Unique { } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl CoerceUnsized> for Unique where T: Unsize { } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl fmt::Pointer for Unique { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&self.as_ptr(), f) } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a mut T> for Unique { fn from(reference: &'a mut T) -> Self { Unique { pointer: NonZero::from(reference), _marker: PhantomData } } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From<&'a T> for Unique { fn from(reference: &'a T) -> Self { Unique { pointer: NonZero::from(reference), _marker: PhantomData } } } -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl<'a, T: ?Sized> From> for Unique { fn from(p: NonNull) -> Self { Unique { pointer: p.pointer, _marker: PhantomData } diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 2c0009569d7..bc7052d676d 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -27,6 +27,7 @@ #![feature(iterator_try_fold)] #![feature(iter_rfind)] #![feature(iter_rfold)] +#![feature(nonnull)] #![feature(nonzero)] #![feature(pattern)] #![feature(raw)] @@ -41,7 +42,6 @@ #![feature(trusted_len)] #![feature(try_from)] #![feature(try_trait)] -#![feature(unique)] #![feature(exact_chunks)] extern crate core; diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs index 98436f0e1d1..00f87336f3c 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -249,9 +249,9 @@ fn test_set_memory() { } #[test] -fn test_unsized_unique() { +fn test_unsized_nonnull() { let xs: &[i32] = &[1, 2, 3]; - let ptr = unsafe { Unique::new_unchecked(xs as *const [i32] as *mut [i32]) }; + let ptr = unsafe { NonNull::new_unchecked(xs as *const [i32] as *mut [i32]) }; let ys = unsafe { ptr.as_ref() }; let zs: &[i32] = &[1, 2, 3]; assert!(ys == zs); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 8a1ba32f7dc..9f65d61658c 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -294,6 +294,7 @@ #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(prelude_import)] +#![feature(ptr_internals)] #![feature(rand)] #![feature(raw)] #![feature(repr_align)] @@ -315,7 +316,6 @@ #![feature(try_from)] #![feature(unboxed_closures)] #![feature(unicode)] -#![feature(unique)] #![feature(untagged_unions)] #![feature(unwind_attributes)] #![feature(vec_push_all)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 68584b7cf25..6f7d8ddb770 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -196,7 +196,7 @@ impl<'a, T: RefUnwindSafe + ?Sized> UnwindSafe for &'a T {} impl UnwindSafe for *const T {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl UnwindSafe for *mut T {} -#[unstable(feature = "unique", issue = "27730")] +#[unstable(feature = "ptr_internals", issue = "0")] impl UnwindSafe for Unique {} #[unstable(feature = "nonnull", issue = "27730")] impl UnwindSafe for NonNull {} diff --git a/src/test/run-pass/issue-23433.rs b/src/test/run-pass/issue-23433.rs index aa13d6fad47..37cc1b134c3 100644 --- a/src/test/run-pass/issue-23433.rs +++ b/src/test/run-pass/issue-23433.rs @@ -10,13 +10,13 @@ // Don't fail if we encounter a NonZero<*T> where T is an unsized type -#![feature(unique)] +#![feature(nonnull)] -use std::ptr::Unique; +use std::ptr::NonNull; fn main() { let mut a = [0u8; 5]; - let b: Option> = Some(Unique::from(&mut a)); + let b: Option> = Some(NonNull::from(&mut a)); match b { Some(_) => println!("Got `Some`"), None => panic!("Unexpected `None`"), -- cgit 1.4.1-3-g733a5 From 55c50cd8ac5ed5a799bc9f5aa1fe8fcfbb956706 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 19:50:21 +0100 Subject: Stabilize std::ptr::NonNull --- src/liballoc/boxed.rs | 10 ++-------- src/liballoc/lib.rs | 1 - src/libcore/ptr.rs | 32 +++++++++++++++++--------------- src/libcore/tests/lib.rs | 1 - src/librustc_data_structures/lib.rs | 1 - src/libstd/lib.rs | 1 - src/libstd/panic.rs | 2 +- src/test/run-pass/issue-23433.rs | 2 -- 8 files changed, 20 insertions(+), 30 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 994466e2249..e7bc10dfaa9 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -288,16 +288,13 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(nonnull)] - /// /// fn main() { /// let x = Box::new(5); /// let ptr = Box::into_nonnull_raw(x); /// let x = unsafe { Box::from_nonnull_raw(ptr) }; /// } /// ``` - #[unstable(feature = "nonnull", reason = "needs an RFC to flesh out design", - issue = "27730")] + #[stable(feature = "nonnull", since = "1.24.0")] #[inline] pub unsafe fn from_nonnull_raw(u: NonNull) -> Self { Box(u.into()) @@ -352,15 +349,12 @@ impl Box { /// # Examples /// /// ``` - /// #![feature(nonnull)] - /// /// fn main() { /// let x = Box::new(5); /// let ptr = Box::into_nonnull_raw(x); /// } /// ``` - #[unstable(feature = "nonnull", reason = "needs an RFC to flesh out design", - issue = "27730")] + #[stable(feature = "nonnull", since = "1.24.0")] #[inline] pub fn into_nonnull_raw(b: Box) -> NonNull { Box::into_unique(b).into() diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 07e4ccc45a9..f25b455f915 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -103,7 +103,6 @@ #![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] -#![feature(nonnull)] #![feature(nonzero)] #![feature(offset_to)] #![feature(optin_builtin_traits)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 6cb84615d09..2e5f36ed71f 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2481,13 +2481,12 @@ pub type Shared = NonNull; /// Usually this won't be necessary; covariance is correct for most safe abstractions, /// such as Box, Rc, Arc, Vec, and LinkedList. This is the case because they /// provide a public API that follows the normal shared XOR mutable rules of Rust. -#[unstable(feature = "shared", reason = "needs an RFC to flesh out design", - issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] pub struct NonNull { pointer: NonZero<*const T>, } -#[unstable(feature = "shared", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl fmt::Debug for NonNull { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{:p}", self.as_ptr()) @@ -2496,20 +2495,20 @@ impl fmt::Debug for NonNull { /// `NonNull` pointers are not `Send` because the data they reference may be aliased. // NB: This impl is unnecessary, but should provide better error messages. -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl !Send for NonNull { } /// `NonNull` pointers are not `Sync` because the data they reference may be aliased. // NB: This impl is unnecessary, but should provide better error messages. -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl !Sync for NonNull { } -#[unstable(feature = "nonnull", issue = "27730")] impl NonNull { /// Creates a new `NonNull` that is dangling, but well-aligned. /// /// This is useful for initializing types which lazily allocate, like /// `Vec::new` does. + #[stable(feature = "nonnull", since = "1.24.0")] pub fn empty() -> Self { unsafe { let ptr = mem::align_of::() as *mut T; @@ -2518,24 +2517,25 @@ impl NonNull { } } -#[unstable(feature = "nonnull", issue = "27730")] impl NonNull { /// Creates a new `NonNull`. /// /// # Safety /// /// `ptr` must be non-null. - #[unstable(feature = "nonnull", issue = "27730")] + #[stable(feature = "nonnull", since = "1.24.0")] pub const unsafe fn new_unchecked(ptr: *mut T) -> Self { NonNull { pointer: NonZero::new_unchecked(ptr) } } /// Creates a new `NonNull` if `ptr` is non-null. + #[stable(feature = "nonnull", since = "1.24.0")] pub fn new(ptr: *mut T) -> Option { NonZero::new(ptr as *const T).map(|nz| NonNull { pointer: nz }) } /// Acquires the underlying `*mut` pointer. + #[stable(feature = "nonnull", since = "1.24.0")] pub fn as_ptr(self) -> *mut T { self.pointer.get() as *mut T } @@ -2545,6 +2545,7 @@ impl NonNull { /// The resulting lifetime is bound to self so this behaves "as if" /// it were actually an instance of T that is getting borrowed. If a longer /// (unbound) lifetime is needed, use `&*my_ptr.ptr()`. + #[stable(feature = "nonnull", since = "1.24.0")] pub unsafe fn as_ref(&self) -> &T { &*self.as_ptr() } @@ -2554,46 +2555,47 @@ impl NonNull { /// The resulting lifetime is bound to self so this behaves "as if" /// it were actually an instance of T that is getting borrowed. If a longer /// (unbound) lifetime is needed, use `&mut *my_ptr.ptr_mut()`. + #[stable(feature = "nonnull", since = "1.24.0")] pub unsafe fn as_mut(&mut self) -> &mut T { &mut *self.as_ptr() } } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl Clone for NonNull { fn clone(&self) -> Self { *self } } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl Copy for NonNull { } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl CoerceUnsized> for NonNull where T: Unsize { } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl fmt::Pointer for NonNull { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&self.as_ptr(), f) } } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl From> for NonNull { fn from(unique: Unique) -> Self { NonNull { pointer: unique.pointer } } } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl<'a, T: ?Sized> From<&'a mut T> for NonNull { fn from(reference: &'a mut T) -> Self { NonNull { pointer: NonZero::from(reference) } } } -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl<'a, T: ?Sized> From<&'a T> for NonNull { fn from(reference: &'a T) -> Self { NonNull { pointer: NonZero::from(reference) } diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index bc7052d676d..1c32452f846 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -27,7 +27,6 @@ #![feature(iterator_try_fold)] #![feature(iter_rfind)] #![feature(iter_rfold)] -#![feature(nonnull)] #![feature(nonzero)] #![feature(pattern)] #![feature(raw)] diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 1d53825ac37..a35ef2f7ce7 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -22,7 +22,6 @@ #![deny(warnings)] #![feature(collections_range)] -#![feature(nonnull)] #![feature(nonzero)] #![feature(unboxed_closures)] #![feature(fn_traits)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9f65d61658c..91cc6d25cce 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -283,7 +283,6 @@ #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] #![feature(never_type)] -#![feature(nonnull)] #![feature(num_bits_bytes)] #![feature(old_wrapping)] #![feature(on_unimplemented)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 6f7d8ddb770..560876006d3 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -198,7 +198,7 @@ impl UnwindSafe for *const T {} impl UnwindSafe for *mut T {} #[unstable(feature = "ptr_internals", issue = "0")] impl UnwindSafe for Unique {} -#[unstable(feature = "nonnull", issue = "27730")] +#[stable(feature = "nonnull", since = "1.24.0")] impl UnwindSafe for NonNull {} #[stable(feature = "catch_unwind", since = "1.9.0")] impl UnwindSafe for Mutex {} diff --git a/src/test/run-pass/issue-23433.rs b/src/test/run-pass/issue-23433.rs index 37cc1b134c3..7af732f561d 100644 --- a/src/test/run-pass/issue-23433.rs +++ b/src/test/run-pass/issue-23433.rs @@ -10,8 +10,6 @@ // Don't fail if we encounter a NonZero<*T> where T is an unsized type -#![feature(nonnull)] - use std::ptr::NonNull; fn main() { -- cgit 1.4.1-3-g733a5 From 1772fa2aa1262e5efae8172bbd9154a937269eb7 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 22 Dec 2017 23:46:52 +0100 Subject: Rename Box::*_nonnull_raw to *_non_null_raw --- src/liballoc/arc.rs | 4 ++-- src/liballoc/boxed.rs | 22 +++++++++++----------- src/liballoc/linked_list.rs | 6 +++--- src/liballoc/rc.rs | 4 ++-- 4 files changed, 18 insertions(+), 18 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index e490f67dd92..95ba517a7a9 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -286,7 +286,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }; - Arc { ptr: Box::into_nonnull_raw(x), phantom: PhantomData } + Arc { ptr: Box::into_non_null_raw(x), phantom: PhantomData } } /// Returns the contained value, if the `Arc` has exactly one strong reference. @@ -991,7 +991,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Box::into_nonnull_raw(box ArcInner { + ptr: Box::into_non_null_raw(box ArcInner { strong: atomic::AtomicUsize::new(0), weak: atomic::AtomicUsize::new(1), data: uninitialized(), diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index e7bc10dfaa9..a1563483f1e 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -290,13 +290,13 @@ impl Box { /// ``` /// fn main() { /// let x = Box::new(5); - /// let ptr = Box::into_nonnull_raw(x); - /// let x = unsafe { Box::from_nonnull_raw(ptr) }; + /// let ptr = Box::into_non_null_raw(x); + /// let x = unsafe { Box::from_non_null_raw(ptr) }; /// } /// ``` #[stable(feature = "nonnull", since = "1.24.0")] #[inline] - pub unsafe fn from_nonnull_raw(u: NonNull) -> Self { + pub unsafe fn from_non_null_raw(u: NonNull) -> Self { Box(u.into()) } @@ -323,7 +323,7 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Box) -> *mut T { - Box::into_nonnull_raw(b).as_ptr() + Box::into_non_null_raw(b).as_ptr() } /// Consumes the `Box`, returning the wrapped pointer as `NonNull`. @@ -333,17 +333,17 @@ impl Box { /// caller should properly destroy `T` and release the memory. The /// proper way to do so is to either convert the `NonNull` pointer: /// - /// - Into a `Box` with the [`Box::from_nonnull_raw`] function. + /// - Into a `Box` with the [`Box::from_non_null_raw`] function. /// /// - Into a raw pointer and back into a `Box` with the [`Box::from_raw`] /// function. /// /// Note: this is an associated function, which means that you have - /// to call it as `Box::into_nonnull_raw(b)` - /// instead of `b.into_nonnull_raw()`. This + /// to call it as `Box::into_non_null_raw(b)` + /// instead of `b.into_non_null_raw()`. This /// is so that there is no conflict with a method on the inner type. /// - /// [`Box::from_nonnull_raw`]: struct.Box.html#method.from_nonnull_raw + /// [`Box::from_non_null_raw`]: struct.Box.html#method.from_non_null_raw /// [`Box::from_raw`]: struct.Box.html#method.from_raw /// /// # Examples @@ -351,16 +351,16 @@ impl Box { /// ``` /// fn main() { /// let x = Box::new(5); - /// let ptr = Box::into_nonnull_raw(x); + /// let ptr = Box::into_non_null_raw(x); /// } /// ``` #[stable(feature = "nonnull", since = "1.24.0")] #[inline] - pub fn into_nonnull_raw(b: Box) -> NonNull { + pub fn into_non_null_raw(b: Box) -> NonNull { Box::into_unique(b).into() } - #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_nonnull_raw instead")] + #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_non_null_raw instead")] #[inline] pub fn into_unique(b: Box) -> Unique { let unique = b.0; diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 4c4a00e53fa..8ec0110060a 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -157,7 +157,7 @@ impl LinkedList { unsafe { node.next = self.head; node.prev = None; - let node = Some(Box::into_nonnull_raw(node)); + let node = Some(Box::into_non_null_raw(node)); match self.head { None => self.tail = node, @@ -192,7 +192,7 @@ impl LinkedList { unsafe { node.next = None; node.prev = self.tail; - let node = Some(Box::into_nonnull_raw(node)); + let node = Some(Box::into_non_null_raw(node)); match self.tail { None => self.head = node, @@ -986,7 +986,7 @@ impl<'a, T> IterMut<'a, T> { Some(prev) => prev, }; - let node = Some(Box::into_nonnull_raw(box Node { + let node = Some(Box::into_non_null_raw(box Node { next: Some(head), prev: Some(prev), element, diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 590a6837905..c3270e4e21d 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -311,7 +311,7 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - ptr: Box::into_nonnull_raw(box RcBox { + ptr: Box::into_non_null_raw(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value, @@ -1190,7 +1190,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Box::into_nonnull_raw(box RcBox { + ptr: Box::into_non_null_raw(box RcBox { strong: Cell::new(0), weak: Cell::new(1), value: uninitialized(), -- cgit 1.4.1-3-g733a5 From 8ef5e549c3cec025c6b7c37931bddd68f2fcab4a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 27 Dec 2017 22:53:27 +0100 Subject: Remove `Box::from_non_null_raw` Per https://github.com/rust-lang/rust/pull/46952#issuecomment-353956225 --- src/liballoc/boxed.rs | 38 +++----------------------------------- 1 file changed, 3 insertions(+), 35 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index a1563483f1e..60998bc677b 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -272,34 +272,6 @@ impl Box { Box(Unique::new_unchecked(raw)) } - /// Constructs a `Box` from a `NonNull` pointer. - /// - /// After calling this function, the memory is owned by a `Box` and `T` can - /// then be destroyed and released upon drop. - /// - /// # Safety - /// - /// A `NonNull` can be safely created via [`NonNull::new`] and thus doesn't - /// necessarily own the data pointed to nor is the data guaranteed to live - /// as long as the pointer. - /// - /// [`NonNull::new`]: ../../core/ptr/struct.NonNull.html#method.new - /// - /// # Examples - /// - /// ``` - /// fn main() { - /// let x = Box::new(5); - /// let ptr = Box::into_non_null_raw(x); - /// let x = unsafe { Box::from_non_null_raw(ptr) }; - /// } - /// ``` - #[stable(feature = "nonnull", since = "1.24.0")] - #[inline] - pub unsafe fn from_non_null_raw(u: NonNull) -> Self { - Box(u.into()) - } - /// Consumes the `Box`, returning the wrapped raw pointer. /// /// After calling this function, the caller is responsible for the @@ -331,19 +303,15 @@ impl Box { /// After calling this function, the caller is responsible for the /// memory previously managed by the `Box`. In particular, the /// caller should properly destroy `T` and release the memory. The - /// proper way to do so is to either convert the `NonNull` pointer: - /// - /// - Into a `Box` with the [`Box::from_non_null_raw`] function. - /// - /// - Into a raw pointer and back into a `Box` with the [`Box::from_raw`] - /// function. + /// proper way to do so is to convert the `NonNull` pointer + /// into a raw pointer and back into a `Box` with the [`Box::from_raw`] + /// function. /// /// Note: this is an associated function, which means that you have /// to call it as `Box::into_non_null_raw(b)` /// instead of `b.into_non_null_raw()`. This /// is so that there is no conflict with a method on the inner type. /// - /// [`Box::from_non_null_raw`]: struct.Box.html#method.from_non_null_raw /// [`Box::from_raw`]: struct.Box.html#method.from_raw /// /// # Examples -- cgit 1.4.1-3-g733a5 From 12b3630f62e2412fa2f7d50b765cd6d711b74064 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 27 Dec 2017 22:56:06 +0100 Subject: Rename Box::into_non_null_raw to Box::into_raw_non_null --- src/liballoc/arc.rs | 4 ++-- src/liballoc/boxed.rs | 12 ++++++------ src/liballoc/linked_list.rs | 6 +++--- src/liballoc/rc.rs | 4 ++-- 4 files changed, 13 insertions(+), 13 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 95ba517a7a9..6a77bf64bae 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -286,7 +286,7 @@ impl Arc { weak: atomic::AtomicUsize::new(1), data, }; - Arc { ptr: Box::into_non_null_raw(x), phantom: PhantomData } + Arc { ptr: Box::into_raw_non_null(x), phantom: PhantomData } } /// Returns the contained value, if the `Arc` has exactly one strong reference. @@ -991,7 +991,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Box::into_non_null_raw(box ArcInner { + ptr: Box::into_raw_non_null(box ArcInner { strong: atomic::AtomicUsize::new(0), weak: atomic::AtomicUsize::new(1), data: uninitialized(), diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 60998bc677b..78a4b337711 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -295,7 +295,7 @@ impl Box { #[stable(feature = "box_raw", since = "1.4.0")] #[inline] pub fn into_raw(b: Box) -> *mut T { - Box::into_non_null_raw(b).as_ptr() + Box::into_raw_non_null(b).as_ptr() } /// Consumes the `Box`, returning the wrapped pointer as `NonNull`. @@ -308,8 +308,8 @@ impl Box { /// function. /// /// Note: this is an associated function, which means that you have - /// to call it as `Box::into_non_null_raw(b)` - /// instead of `b.into_non_null_raw()`. This + /// to call it as `Box::into_raw_non_null(b)` + /// instead of `b.into_raw_non_null()`. This /// is so that there is no conflict with a method on the inner type. /// /// [`Box::from_raw`]: struct.Box.html#method.from_raw @@ -319,16 +319,16 @@ impl Box { /// ``` /// fn main() { /// let x = Box::new(5); - /// let ptr = Box::into_non_null_raw(x); + /// let ptr = Box::into_raw_non_null(x); /// } /// ``` #[stable(feature = "nonnull", since = "1.24.0")] #[inline] - pub fn into_non_null_raw(b: Box) -> NonNull { + pub fn into_raw_non_null(b: Box) -> NonNull { Box::into_unique(b).into() } - #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_non_null_raw instead")] + #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_raw_non_null instead")] #[inline] pub fn into_unique(b: Box) -> Unique { let unique = b.0; diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 8ec0110060a..3cc810a055f 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -157,7 +157,7 @@ impl LinkedList { unsafe { node.next = self.head; node.prev = None; - let node = Some(Box::into_non_null_raw(node)); + let node = Some(Box::into_raw_non_null(node)); match self.head { None => self.tail = node, @@ -192,7 +192,7 @@ impl LinkedList { unsafe { node.next = None; node.prev = self.tail; - let node = Some(Box::into_non_null_raw(node)); + let node = Some(Box::into_raw_non_null(node)); match self.tail { None => self.head = node, @@ -986,7 +986,7 @@ impl<'a, T> IterMut<'a, T> { Some(prev) => prev, }; - let node = Some(Box::into_non_null_raw(box Node { + let node = Some(Box::into_raw_non_null(box Node { next: Some(head), prev: Some(prev), element, diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index c3270e4e21d..1fa5d34cb57 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -311,7 +311,7 @@ impl Rc { // pointers, which ensures that the weak destructor never frees // the allocation while the strong destructor is running, even // if the weak pointer is stored inside the strong one. - ptr: Box::into_non_null_raw(box RcBox { + ptr: Box::into_raw_non_null(box RcBox { strong: Cell::new(1), weak: Cell::new(1), value, @@ -1190,7 +1190,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Box::into_non_null_raw(box RcBox { + ptr: Box::into_raw_non_null(box RcBox { strong: Cell::new(0), weak: Cell::new(1), value: uninitialized(), -- cgit 1.4.1-3-g733a5 From 5aeeafff49bf099e95caf94719fabcc5008ab95a Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 10 Jan 2018 09:25:11 +0100 Subject: Revert Box::into_raw_non_null to unstable --- src/liballoc/boxed.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 78a4b337711..1f1e98a2b60 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -322,7 +322,7 @@ impl Box { /// let ptr = Box::into_raw_non_null(x); /// } /// ``` - #[stable(feature = "nonnull", since = "1.24.0")] + #[unstable(feature = "nonnull", issue = "27730")] #[inline] pub fn into_raw_non_null(b: Box) -> NonNull { Box::into_unique(b).into() -- cgit 1.4.1-3-g733a5 From 602a445b92b37ec6af4d3d7f331e1a0d1360b8d2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 10 Jan 2018 21:11:55 +0100 Subject: Assign its own tracking issue to Box::into_raw_non_null https://github.com/rust-lang/rust/issues/47336 --- src/liballoc/boxed.rs | 4 +++- src/liballoc/lib.rs | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 1f1e98a2b60..bfe23ddeca3 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -317,12 +317,14 @@ impl Box { /// # Examples /// /// ``` + /// #![feature(box_into_raw_non_null)] + /// /// fn main() { /// let x = Box::new(5); /// let ptr = Box::into_raw_non_null(x); /// } /// ``` - #[unstable(feature = "nonnull", issue = "27730")] + #[unstable(feature = "box_into_raw_non_null", issue = "47336")] #[inline] pub fn into_raw_non_null(b: Box) -> NonNull { Box::into_unique(b).into() diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f25b455f915..5139e54b560 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -84,6 +84,7 @@ #![cfg_attr(test, feature(rand, test))] #![feature(allow_internal_unstable)] #![feature(ascii_ctype)] +#![feature(box_into_raw_non_null)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] -- cgit 1.4.1-3-g733a5 From 4d08d054c7107eaf22e320f60487a616337d34a6 Mon Sep 17 00:00:00 2001 From: Andrew Straw Date: Sat, 20 Jan 2018 23:23:44 +0100 Subject: fix doctests for BTreeSet to use BTreeSet (not BTreeMap) This fixes #47624 --- src/liballoc/btree/set.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs index e094070fc3d..327eaaf4651 100644 --- a/src/liballoc/btree/set.rs +++ b/src/liballoc/btree/set.rs @@ -658,26 +658,26 @@ impl BTreeSet { /// Basic usage: /// /// ``` - /// use std::collections::BTreeMap; + /// use std::collections::BTreeSet; /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// a.insert(3, "c"); - /// a.insert(17, "d"); - /// a.insert(41, "e"); + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// a.insert(3); + /// a.insert(17); + /// a.insert(41); /// /// let b = a.split_off(&3); /// /// assert_eq!(a.len(), 2); /// assert_eq!(b.len(), 3); /// - /// assert_eq!(a[&1], "a"); - /// assert_eq!(a[&2], "b"); + /// assert!(a.contains(&1)); + /// assert!(a.contains(&2)); /// - /// assert_eq!(b[&3], "c"); - /// assert_eq!(b[&17], "d"); - /// assert_eq!(b[&41], "e"); + /// assert!(b.contains(&3)); + /// assert!(b.contains(&17)); + /// assert!(b.contains(&41)); /// ``` #[stable(feature = "btree_split_off", since = "1.11.0")] pub fn split_off(&mut self, key: &Q) -> Self where T: Borrow { -- cgit 1.4.1-3-g733a5 From 1756f680b0c313a3dbfb40070c0160da2ed0576f Mon Sep 17 00:00:00 2001 From: Sebastian Dröge Date: Sun, 21 Jan 2018 11:20:19 +0200 Subject: Fix broken links to other slice functions in chunks/chunks_mut/exact_chunk/exact_chunks_mut docs See https://github.com/rust-lang/rust/pull/47126#discussion_r162780492 --- src/liballoc/slice.rs | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 861f72bcf88..028983de556 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -630,6 +630,8 @@ impl [T] { /// assert_eq!(iter.next().unwrap(), &['m']); /// assert!(iter.next().is_none()); /// ``` + /// + /// [`exact_chunks`]: #method.exact_chunks #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks { @@ -660,6 +662,8 @@ impl [T] { /// assert_eq!(iter.next().unwrap(), &['r', 'e']); /// assert!(iter.next().is_none()); /// ``` + /// + /// [`chunks`]: #method.chunks #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { @@ -692,6 +696,8 @@ impl [T] { /// } /// assert_eq!(v, &[1, 1, 2, 2, 3]); /// ``` + /// + /// [`exact_chunks_mut`]: #method.exact_chunks_mut #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { @@ -728,6 +734,8 @@ impl [T] { /// } /// assert_eq!(v, &[1, 1, 2, 2, 0]); /// ``` + /// + /// [`chunks_mut`]: #method.chunks_mut #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { -- cgit 1.4.1-3-g733a5 From ea814b84630ee878c18ad1abff47a0e5236a2ad5 Mon Sep 17 00:00:00 2001 From: Pieter Penninckx Date: Sun, 21 Jan 2018 15:05:53 +0100 Subject: Revert change to docs in panic section of VecDeque::split_off --- src/liballoc/vec_deque.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 7d15b790b24..4b167bd20bf 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -1635,7 +1635,7 @@ impl VecDeque { /// /// # Panics /// - /// Panics if `at` is out of bounds. + /// Panics if `at > len`. /// /// # Examples /// -- cgit 1.4.1-3-g733a5 From 651ea8ea44d8ac8a02dc357412eb73f830057cae Mon Sep 17 00:00:00 2001 From: Cameron Hart Date: Tue, 26 Dec 2017 10:24:23 +1100 Subject: Stabilized `#[repr(align(x))]` attribute (RFC 1358) --- src/liballoc/tests/lib.rs | 1 - src/libstd/lib.rs | 2 +- src/libsyntax/feature_gate.rs | 30 ++++++++++++---------- src/test/codegen/align-struct.rs | 3 --- src/test/compile-fail/conflicting-repr-hints.rs | 2 -- src/test/compile-fail/repr-align.rs | 2 -- .../compile-fail/repr-packed-contains-align.rs | 2 -- src/test/run-pass/align-struct.rs | 2 -- src/test/run-pass/union/union-align.rs | 2 -- src/test/ui/feature-gate-repr_align.rs | 15 ----------- src/test/ui/feature-gate-repr_align.stderr | 10 -------- src/test/ui/print_type_sizes/repr-align.rs | 2 -- src/test/ui/span/gated-features-attr-spans.rs | 2 +- src/test/ui/span/gated-features-attr-spans.stderr | 10 +------- 14 files changed, 20 insertions(+), 65 deletions(-) delete mode 100644 src/test/ui/feature-gate-repr_align.rs delete mode 100644 src/test/ui/feature-gate-repr_align.stderr (limited to 'src/liballoc') diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index eee229bc6fd..427a7adcbde 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -23,7 +23,6 @@ #![feature(pattern)] #![feature(placement_in_syntax)] #![feature(rand)] -#![feature(repr_align)] #![feature(slice_rotate)] #![feature(splice)] #![feature(str_escape)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 91cc6d25cce..a8049e676b3 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -296,7 +296,6 @@ #![feature(ptr_internals)] #![feature(rand)] #![feature(raw)] -#![feature(repr_align)] #![feature(rustc_attrs)] #![feature(sip_hash_13)] #![feature(slice_bytes)] @@ -323,6 +322,7 @@ #![feature(doc_spotlight)] #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] +#![cfg_attr(stage0, feature(repr_align))] #![default_lib_allocator] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index ac5a10ec703..5a7b53153fd 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -343,9 +343,6 @@ declare_features! ( // Allows the `catch {...}` expression (active, catch_expr, "1.17.0", Some(31436)), - // Allows `repr(align(u16))` struct attribute (RFC 1358) - (active, repr_align, "1.17.0", Some(33626)), - // Used to preserve symbols (see llvm.used) (active, used, "1.18.0", Some(40289)), @@ -546,6 +543,8 @@ declare_features! ( // Allows the sysV64 ABI to be specified on all platforms // instead of just the platforms on which it is the C ABI (accepted, abi_sysv64, "1.24.0", Some(36167)), + // Allows `repr(align(16))` struct attribute (RFC 1358) + (accepted, repr_align, "1.24.0", Some(33626)), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1456,15 +1455,25 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } } + // allow attr_literals in #[repr(align(x))] + let mut is_repr_align = false; + if attr.path == "repr" { + if let Some(content) = attr.meta_item_list() { + is_repr_align = content.iter().any(|c| c.check_name("align")); + } + } + if self.context.features.proc_macro && attr::is_known(attr) { return } - let meta = panictry!(attr.parse_meta(self.context.parse_sess)); - if contains_novel_literal(&meta) { - gate_feature_post!(&self, attr_literals, attr.span, - "non-string literals in attributes, or string \ - literals in top-level positions, are experimental"); + if !is_repr_align { + let meta = panictry!(attr.parse_meta(self.context.parse_sess)); + if contains_novel_literal(&meta) { + gate_feature_post!(&self, attr_literals, attr.span, + "non-string literals in attributes, or string \ + literals in top-level positions, are experimental"); + } } } @@ -1522,11 +1531,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, repr_simd, attr.span, "SIMD types are experimental and possibly buggy"); } - if item.check_name("align") { - gate_feature_post!(&self, repr_align, attr.span, - "the struct `#[repr(align(u16))]` attribute \ - is experimental"); - } if item.check_name("transparent") { gate_feature_post!(&self, repr_transparent, attr.span, "the `#[repr(transparent)]` attribute \ diff --git a/src/test/codegen/align-struct.rs b/src/test/codegen/align-struct.rs index ab9f5dda3a1..155319cb154 100644 --- a/src/test/codegen/align-struct.rs +++ b/src/test/codegen/align-struct.rs @@ -13,9 +13,6 @@ #![crate_type = "lib"] -#![feature(attr_literals)] -#![feature(repr_align)] - #[repr(align(64))] pub struct Align64(i32); // CHECK: %Align64 = type { [0 x i32], i32, [15 x i32] } diff --git a/src/test/compile-fail/conflicting-repr-hints.rs b/src/test/compile-fail/conflicting-repr-hints.rs index 12ac8fb57b1..8acc8b7bb1e 100644 --- a/src/test/compile-fail/conflicting-repr-hints.rs +++ b/src/test/compile-fail/conflicting-repr-hints.rs @@ -9,8 +9,6 @@ // except according to those terms. #![allow(dead_code)] -#![feature(attr_literals)] -#![feature(repr_align)] #[repr(C)] enum A { A } diff --git a/src/test/compile-fail/repr-align.rs b/src/test/compile-fail/repr-align.rs index bc9cf065e5a..7c8eb6a2de9 100644 --- a/src/test/compile-fail/repr-align.rs +++ b/src/test/compile-fail/repr-align.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. #![allow(dead_code)] -#![feature(attr_literals)] -#![feature(repr_align)] #[repr(align(16.0))] //~ ERROR: invalid `repr(align)` attribute: not an unsuffixed integer struct A(i32); diff --git a/src/test/compile-fail/repr-packed-contains-align.rs b/src/test/compile-fail/repr-packed-contains-align.rs index 78d43064ea3..27890333a51 100644 --- a/src/test/compile-fail/repr-packed-contains-align.rs +++ b/src/test/compile-fail/repr-packed-contains-align.rs @@ -7,8 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(attr_literals)] -#![feature(repr_align)] #![feature(untagged_unions)] #![allow(dead_code)] diff --git a/src/test/run-pass/align-struct.rs b/src/test/run-pass/align-struct.rs index e42aa868c47..dea8462705f 100644 --- a/src/test/run-pass/align-struct.rs +++ b/src/test/run-pass/align-struct.rs @@ -7,8 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(attr_literals)] -#![feature(repr_align)] #![feature(box_syntax)] use std::mem; diff --git a/src/test/run-pass/union/union-align.rs b/src/test/run-pass/union/union-align.rs index c0100df53e7..54e4e12d24f 100644 --- a/src/test/run-pass/union/union-align.rs +++ b/src/test/run-pass/union/union-align.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(attr_literals)] -#![feature(repr_align)] #![feature(untagged_unions)] use std::mem::{size_of, size_of_val, align_of, align_of_val}; diff --git a/src/test/ui/feature-gate-repr_align.rs b/src/test/ui/feature-gate-repr_align.rs deleted file mode 100644 index 9591d367a2d..00000000000 --- a/src/test/ui/feature-gate-repr_align.rs +++ /dev/null @@ -1,15 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -#![feature(attr_literals)] - -#[repr(align(64))] //~ error: the struct `#[repr(align(u16))]` attribute is experimental -struct Foo(u64, u64); - -fn main() {} diff --git a/src/test/ui/feature-gate-repr_align.stderr b/src/test/ui/feature-gate-repr_align.stderr deleted file mode 100644 index dd88067d58f..00000000000 --- a/src/test/ui/feature-gate-repr_align.stderr +++ /dev/null @@ -1,10 +0,0 @@ -error[E0658]: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33626) - --> $DIR/feature-gate-repr_align.rs:12:1 - | -12 | #[repr(align(64))] //~ error: the struct `#[repr(align(u16))]` attribute is experimental - | ^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(repr_align)] to the crate attributes to enable - -error: aborting due to previous error - diff --git a/src/test/ui/print_type_sizes/repr-align.rs b/src/test/ui/print_type_sizes/repr-align.rs index 108b8dbba01..92928bba1c3 100644 --- a/src/test/ui/print_type_sizes/repr-align.rs +++ b/src/test/ui/print_type_sizes/repr-align.rs @@ -18,8 +18,6 @@ // It avoids using u64/i64 because on some targets that is only 4-byte // aligned (while on most it is 8-byte aligned) and so the resulting // padding and overall computed sizes can be quite different. -#![feature(attr_literals)] -#![feature(repr_align)] #![feature(start)] #![allow(dead_code)] diff --git a/src/test/ui/span/gated-features-attr-spans.rs b/src/test/ui/span/gated-features-attr-spans.rs index ace185d0169..83a4c5d5dd2 100644 --- a/src/test/ui/span/gated-features-attr-spans.rs +++ b/src/test/ui/span/gated-features-attr-spans.rs @@ -10,7 +10,7 @@ #![feature(attr_literals)] -#[repr(align(16))] //~ ERROR is experimental +#[repr(align(16))] struct Gem { mohs_hardness: u8, poofed: bool, diff --git a/src/test/ui/span/gated-features-attr-spans.stderr b/src/test/ui/span/gated-features-attr-spans.stderr index 74a2c1d742b..f15c4a72c5a 100644 --- a/src/test/ui/span/gated-features-attr-spans.stderr +++ b/src/test/ui/span/gated-features-attr-spans.stderr @@ -1,11 +1,3 @@ -error[E0658]: the struct `#[repr(align(u16))]` attribute is experimental (see issue #33626) - --> $DIR/gated-features-attr-spans.rs:13:1 - | -13 | #[repr(align(16))] //~ ERROR is experimental - | ^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(repr_align)] to the crate attributes to enable - error[E0658]: SIMD types are experimental and possibly buggy (see issue #27731) --> $DIR/gated-features-attr-spans.rs:20:1 | @@ -30,5 +22,5 @@ warning: `#[must_use]` on functions is experimental (see issue #43302) | = help: add #![feature(fn_must_use)] to the crate attributes to enable -error: aborting due to 2 previous errors +error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From 399dcd112725a35352075262863781b3355452cd Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 24 Jan 2018 22:25:42 +0100 Subject: Add missing micro version number component in stability attributes. --- src/liballoc/heap.rs | 2 +- src/libcore/panic.rs | 2 +- src/libcore/ptr.rs | 2 +- src/libstd/net/mod.rs | 8 ++++---- 4 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 37af9ea5295..372d606e457 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -232,7 +232,7 @@ unsafe impl Alloc for Heap { /// /// This preserves the non-null invariant for types like `Box`. The address /// may overlap with non-zero-size memory allocations. -#[rustc_deprecated(since = "1.19", reason = "Use Unique/NonNull::empty() instead")] +#[rustc_deprecated(since = "1.19.0", reason = "Use Unique/NonNull::empty() instead")] #[unstable(feature = "heap_api", issue = "27700")] pub const EMPTY: *mut () = 1 as *mut (); diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 14eb68d9b95..4e72eaa57c7 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -238,7 +238,7 @@ impl<'a> Location<'a> { /// /// panic!("Normal panic"); /// ``` - #[stable(feature = "panic_col", since = "1.25")] + #[stable(feature = "panic_col", since = "1.25.0")] pub fn column(&self) -> u32 { self.col } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index fab5832d905..2c1dfb13633 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2461,7 +2461,7 @@ impl<'a, T: ?Sized> From> for Unique { } /// Previous name of `NonNull`. -#[rustc_deprecated(since = "1.24", reason = "renamed to `NonNull`")] +#[rustc_deprecated(since = "1.25.0", reason = "renamed to `NonNull`")] #[unstable(feature = "shared", issue = "27730")] pub type Shared = NonNull; diff --git a/src/libstd/net/mod.rs b/src/libstd/net/mod.rs index eb0e2e13b4c..eef043683b0 100644 --- a/src/libstd/net/mod.rs +++ b/src/libstd/net/mod.rs @@ -134,14 +134,14 @@ fn each_addr(addr: A, mut f: F) -> io::Result iterator and returning socket \ addresses", issue = "27705")] -#[rustc_deprecated(since = "1.25", reason = "Use the ToSocketAddrs trait instead")] +#[rustc_deprecated(since = "1.25.0", reason = "Use the ToSocketAddrs trait instead")] pub struct LookupHost(net_imp::LookupHost); #[unstable(feature = "lookup_host", reason = "unsure about the returned \ iterator and returning socket \ addresses", issue = "27705")] -#[rustc_deprecated(since = "1.25", reason = "Use the ToSocketAddrs trait instead")] +#[rustc_deprecated(since = "1.25.0", reason = "Use the ToSocketAddrs trait instead")] #[allow(deprecated)] impl Iterator for LookupHost { type Item = SocketAddr; @@ -152,7 +152,7 @@ impl Iterator for LookupHost { iterator and returning socket \ addresses", issue = "27705")] -#[rustc_deprecated(since = "1.25", reason = "Use the ToSocketAddrs trait instead")] +#[rustc_deprecated(since = "1.25.0", reason = "Use the ToSocketAddrs trait instead")] #[allow(deprecated)] impl fmt::Debug for LookupHost { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -186,7 +186,7 @@ impl fmt::Debug for LookupHost { iterator and returning socket \ addresses", issue = "27705")] -#[rustc_deprecated(since = "1.25", reason = "Use the ToSocketAddrs trait instead")] +#[rustc_deprecated(since = "1.25.0", reason = "Use the ToSocketAddrs trait instead")] #[allow(deprecated)] pub fn lookup_host(host: &str) -> io::Result { net_imp::lookup_host(host).map(LookupHost) -- cgit 1.4.1-3-g733a5 From 7b4cbbd12d88c8e64d9c7aa1e326c7c78f2a7ed9 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sun, 28 Jan 2018 21:50:01 -0500 Subject: Document that `Index` ops can panic on `HashMap` & `BTreeMap`. Fixes https://github.com/rust-lang/rust/issues/47011. --- src/liballoc/btree/map.rs | 5 +++++ src/libstd/collections/hash/map.rs | 9 +++++++-- 2 files changed, 12 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b114dc640fb..b320bed5432 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -1748,6 +1748,11 @@ impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap { type Output = V; + /// Returns a reference to the value corresponding to the supplied key. + /// + /// # Panics + /// + /// Panics if the key is not present in the `BTreeMap`. #[inline] fn index(&self, key: &Q) -> &V { self.get(key).expect("no entry found for key") diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index b01420f36a0..82a687ae5e4 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1384,9 +1384,14 @@ impl<'a, K, Q: ?Sized, V, S> Index<&'a Q> for HashMap { type Output = V; + /// Returns a reference to the value corresponding to the supplied key. + /// + /// # Panics + /// + /// Panics if the key is not present in the `HashMap`. #[inline] - fn index(&self, index: &Q) -> &V { - self.get(index).expect("no entry found for key") + fn index(&self, key: &Q) -> &V { + self.get(key).expect("no entry found for key") } } -- cgit 1.4.1-3-g733a5 From 2a4c0185187dd40683697932c57af608062cb320 Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Wed, 7 Feb 2018 12:35:52 -0700 Subject: Apply optimization from #44355 to retain --- src/liballoc/vec.rs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index b26979c7f6d..a906628dbc7 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -813,14 +813,19 @@ impl Vec { for i in 0..len { if !f(&v[i]) { del += 1; + unsafe { + ptr::read(&v[i]); + } } else if del > 0 { - v.swap(i - del, i); + let src: *const T = &v[i]; + let dst: *mut T = &mut v[i - del]; + unsafe { + ptr::copy_nonoverlapping(src, dst, 1); + } } } } - if del > 0 { - self.truncate(len - del); - } + self.len = len - del; } /// Removes all but the first of consecutive elements in the vector that resolve to the same -- cgit 1.4.1-3-g733a5 From a67749ae87b1c873ed09fca2a204beff2fe5e7ea Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Thu, 8 Feb 2018 08:27:53 -0700 Subject: Swap `ptr::read` for `ptr::drop_in_place` --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index a906628dbc7..41ba8e12105 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -814,7 +814,7 @@ impl Vec { if !f(&v[i]) { del += 1; unsafe { - ptr::read(&v[i]); + ptr::drop_in_place(&mut v[i]); } } else if del > 0 { let src: *const T = &v[i]; -- cgit 1.4.1-3-g733a5 From 467b5cfcb70c6a0f4b681f90219ce7d490980a47 Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 10 Feb 2018 01:26:19 +0100 Subject: stabilize (version: 1.26.0) Box::leak, cc #46179 --- src/liballoc/boxed.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index cdaad973a71..a831391d168 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -381,8 +381,7 @@ impl Box { /// assert_eq!(*static_ref, [4, 2, 3]); /// } /// ``` - #[unstable(feature = "box_leak", reason = "needs an FCP to stabilize", - issue = "46179")] + #[stable(feature = "box_leak", since = "1.26.0")] #[inline] pub fn leak<'a>(b: Box) -> &'a mut T where -- cgit 1.4.1-3-g733a5 From 486160335c70c4a4b39ce8262314bc1bd63012ca Mon Sep 17 00:00:00 2001 From: Mazdak Farrokhzad Date: Sat, 10 Feb 2018 01:39:22 +0100 Subject: stabilize Box::leak: remove #![feature(box_leak)] in docs --- src/liballoc/boxed.rs | 4 ---- 1 file changed, 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index a831391d168..75a59de337c 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -359,8 +359,6 @@ impl Box { /// Simple usage: /// /// ``` - /// #![feature(box_leak)] - /// /// fn main() { /// let x = Box::new(41); /// let static_ref: &'static mut usize = Box::leak(x); @@ -372,8 +370,6 @@ impl Box { /// Unsized data: /// /// ``` - /// #![feature(box_leak)] - /// /// fn main() { /// let x = vec![1, 2, 3].into_boxed_slice(); /// let static_ref = Box::leak(x); -- cgit 1.4.1-3-g733a5 From 770fdeddb4ca7cedca41a391d4b283069e1d9a8c Mon Sep 17 00:00:00 2001 From: hedgehog1024 Date: Mon, 12 Feb 2018 22:19:37 +0300 Subject: Stabilize 'entry_and_modify' feature for BTreeMap --- src/liballoc/btree/map.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b320bed5432..b98489c516a 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2114,7 +2114,6 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// # Examples /// /// ``` - /// #![feature(entry_and_modify)] /// use std::collections::BTreeMap; /// /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); @@ -2129,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[unstable(feature = "entry_and_modify", issue = "44733")] + #[stable(feature = "entry_and_modify", since = "1.25.0")] pub fn and_modify(self, mut f: F) -> Self where F: FnMut(&mut V) { -- cgit 1.4.1-3-g733a5 From fbad3b2468f46c14d0fd1283aa4935b3d79f007b Mon Sep 17 00:00:00 2001 From: Jacob Kiesel Date: Tue, 13 Feb 2018 08:48:25 -0700 Subject: Switch to retain calling drain_filter. --- src/liballoc/vec.rs | 22 +--------------------- 1 file changed, 1 insertion(+), 21 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 41ba8e12105..5c7f8ef7321 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -805,27 +805,7 @@ impl Vec { pub fn retain(&mut self, mut f: F) where F: FnMut(&T) -> bool { - let len = self.len(); - let mut del = 0; - { - let v = &mut **self; - - for i in 0..len { - if !f(&v[i]) { - del += 1; - unsafe { - ptr::drop_in_place(&mut v[i]); - } - } else if del > 0 { - let src: *const T = &v[i]; - let dst: *mut T = &mut v[i - del]; - unsafe { - ptr::copy_nonoverlapping(src, dst, 1); - } - } - } - } - self.len = len - del; + self.drain_filter(|x| !f(x)); } /// Removes all but the first of consecutive elements in the vector that resolve to the same -- cgit 1.4.1-3-g733a5 From 22aecb9c6e894b47935e1c10ece647146c41ded8 Mon Sep 17 00:00:00 2001 From: Sergio Benitez Date: Fri, 16 Feb 2018 17:05:49 -0800 Subject: Clarify contiguity of Vec's elements. --- src/liballoc/vec.rs | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 5c7f8ef7321..39a4d271bd6 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -231,9 +231,9 @@ use Bound::{Excluded, Included, Unbounded}; /// /// If a `Vec` *has* allocated memory, then the memory it points to is on the heap /// (as defined by the allocator Rust is configured to use by default), and its -/// pointer points to [`len`] initialized elements in order (what you would see -/// if you coerced it to a slice), followed by [`capacity`]` - `[`len`] -/// logically uninitialized elements. +/// pointer points to [`len`] initialized, contiguous elements in order (what +/// you would see if you coerced it to a slice), followed by [`capacity`]` - +/// `[`len`] logically uninitialized, contiguous elements. /// /// `Vec` will never perform a "small optimization" where elements are actually /// stored on the stack for two reasons: @@ -281,8 +281,8 @@ use Bound::{Excluded, Included, Unbounded}; /// not break, however: using `unsafe` code to write to the excess capacity, /// and then increasing the length to match, is always valid. /// -/// `Vec` does not currently guarantee the order in which elements are dropped -/// (the order has changed in the past, and may change again). +/// `Vec` does not currently guarantee the order in which elements are dropped. +/// The order has changed in the past and may change again. /// /// [`vec!`]: ../../std/macro.vec.html /// [`Index`]: ../../std/ops/trait.Index.html -- cgit 1.4.1-3-g733a5 From e88fe1d5199b8704342e80e8effcdfbdf3ca020a Mon Sep 17 00:00:00 2001 From: Anthony Deschamps Date: Thu, 22 Feb 2018 14:21:54 -0500 Subject: Small grammar fix to docs for String::new(). --- src/liballoc/string.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 8d99d0bc8f4..409d2ab287e 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -364,7 +364,7 @@ impl String { /// /// Given that the `String` is empty, this will not allocate any initial /// buffer. While that means that this initial operation is very - /// inexpensive, but may cause excessive allocation later, when you add + /// inexpensive, it may cause excessive allocation later when you add /// data. If you have an idea of how much data the `String` will hold, /// consider the [`with_capacity`] method to prevent excessive /// re-allocation. -- cgit 1.4.1-3-g733a5 From 311fbc9265ea1ebe803449c8d7e083f9edf605d9 Mon Sep 17 00:00:00 2001 From: Matt Brubeck Date: Thu, 22 Feb 2018 12:05:30 -0800 Subject: [docs] Minor wording changes to drain_filter docs The docs currently say, "If the closure returns false, it will try again, and call the closure on the next element." But this happens regardless of whether the closure returns true or false. --- src/liballoc/linked_list.rs | 4 ++-- src/liballoc/vec.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 65be087b35e..ec579e3fd68 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -747,8 +747,8 @@ impl LinkedList { /// Creates an iterator which uses a closure to determine if an element should be removed. /// /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, it will try again, and call the closure on the next element, - /// seeing if it passes the test. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. /// /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of /// whether you choose to keep or remove it. diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 39a4d271bd6..3c9b6b94b44 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1966,8 +1966,8 @@ impl Vec { /// Creates an iterator which uses a closure to determine if an element should be removed. /// /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, it will try again, and call the closure - /// on the next element, seeing if it passes the test. + /// If the closure returns false, the element will remain in the vector and will not be yielded + /// by the iterator. /// /// Using this method is equivalent to the following code: /// -- cgit 1.4.1-3-g733a5 From b1a6c8bdd3a76207eff76b004945bc2a13755eca Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Thu, 22 Feb 2018 19:53:44 -0500 Subject: Stabilize [T]::rotate_{left,right} https://github.com/rust-lang/rust/issues/41891 --- src/liballoc/benches/lib.rs | 1 - src/liballoc/lib.rs | 1 - src/liballoc/slice.rs | 20 +++----------------- src/liballoc/tests/lib.rs | 1 - src/libcore/slice/mod.rs | 4 ++-- src/libcore/tests/lib.rs | 1 - 6 files changed, 5 insertions(+), 23 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 174628ccd07..2de0ffb4b26 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -13,7 +13,6 @@ #![feature(i128_type)] #![feature(rand)] #![feature(repr_simd)] -#![feature(slice_rotate)] #![feature(test)] extern crate rand; diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 5139e54b560..d250cfe1880 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -79,7 +79,6 @@ #![cfg_attr(test, feature(placement_in))] #![cfg_attr(not(test), feature(core_float))] #![cfg_attr(not(test), feature(exact_size_is_empty))] -#![cfg_attr(not(test), feature(slice_rotate))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] #![feature(allow_internal_unstable)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 028983de556..dc40062ef13 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1460,8 +1460,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_rotate)] - /// /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; /// a.rotate_left(2); /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']); @@ -1470,23 +1468,15 @@ impl [T] { /// Rotating a subslice: /// /// ``` - /// #![feature(slice_rotate)] - /// /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; /// a[1..5].rotate_left(1); /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); - /// ``` - #[unstable(feature = "slice_rotate", issue = "41891")] + /// ``` + #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_left(&mut self, mid: usize) { core_slice::SliceExt::rotate_left(self, mid); } - #[unstable(feature = "slice_rotate", issue = "41891")] - #[rustc_deprecated(since = "", reason = "renamed to `rotate_left`")] - pub fn rotate(&mut self, mid: usize) { - core_slice::SliceExt::rotate_left(self, mid); - } - /// Rotates the slice in-place such that the first `self.len() - k` /// elements of the slice move to the end while the last `k` elements move /// to the front. After calling `rotate_right`, the element previously at @@ -1505,8 +1495,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_rotate)] - /// /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; /// a.rotate_right(2); /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']); @@ -1515,13 +1503,11 @@ impl [T] { /// Rotate a subslice: /// /// ``` - /// #![feature(slice_rotate)] - /// /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; /// a[1..5].rotate_right(1); /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); /// ``` - #[unstable(feature = "slice_rotate", issue = "41891")] + #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_right(&mut self, k: usize) { core_slice::SliceExt::rotate_right(self, k); } diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 427a7adcbde..168dbb2ce9b 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -23,7 +23,6 @@ #![feature(pattern)] #![feature(placement_in_syntax)] #![feature(rand)] -#![feature(slice_rotate)] #![feature(splice)] #![feature(str_escape)] #![feature(string_retain)] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index ac390313a67..a43ed65907f 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -211,10 +211,10 @@ pub trait SliceExt { #[stable(feature = "core", since = "1.6.0")] fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - #[unstable(feature = "slice_rotate", issue = "41891")] + #[stable(feature = "slice_rotate", since = "1.26.0")] fn rotate_left(&mut self, mid: usize); - #[unstable(feature = "slice_rotate", issue = "41891")] + #[stable(feature = "slice_rotate", since = "1.26.0")] fn rotate_right(&mut self, k: usize); #[stable(feature = "clone_from_slice", since = "1.7.0")] diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 3e901a9d442..cb8bac10d2b 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -35,7 +35,6 @@ #![feature(refcell_replace_swap)] #![feature(sip_hash_13)] #![feature(slice_patterns)] -#![feature(slice_rotate)] #![feature(sort_internals)] #![feature(specialization)] #![feature(step_trait)] -- cgit 1.4.1-3-g733a5 From 0aa753ba30254631ce05f37cd2ff0dd428d931c5 Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Sat, 24 Feb 2018 21:06:29 -0800 Subject: 1.25.0 -> 1.26.- --- src/liballoc/btree/map.rs | 2 +- src/libstd/collections/hash/map.rs | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index b98489c516a..618ef81fdd9 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2128,7 +2128,7 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[stable(feature = "entry_and_modify", since = "1.25.0")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify(self, mut f: F) -> Self where F: FnMut(&mut V) { diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 80e52123a01..b63a45ecade 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2080,7 +2080,7 @@ impl<'a, K, V> Entry<'a, K, V> { /// .or_insert(42); /// assert_eq!(map["poneyland"], 43); /// ``` - #[stable(feature = "entry_and_modify", since = "1.25.0")] + #[stable(feature = "entry_and_modify", since = "1.26.0")] pub fn and_modify(self, mut f: F) -> Self where F: FnMut(&mut V) { -- cgit 1.4.1-3-g733a5 From 08504fbb0b05abdd9543f08102b0d6275dde210c Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Fri, 2 Mar 2018 13:50:59 +0900 Subject: Optimize str::repeat --- src/liballoc/lib.rs | 1 + src/liballoc/str.rs | 37 ++++++++++++++++++++++++++++++++++--- 2 files changed, 35 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d250cfe1880..cb43d5bee78 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -124,6 +124,7 @@ #![feature(allocator_internals)] #![feature(on_unimplemented)] #![feature(exact_chunks)] +#![feature(pointer_methods)] #![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))] #![cfg_attr(test, feature(test, box_heap))] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index a00e3d17dd0..08ba4a180ed 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -43,6 +43,7 @@ use core::str as core_str; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; +use core::ptr; use core::iter::FusedIterator; use std_unicode::str::{UnicodeStr, Utf16Encoder}; @@ -2066,9 +2067,39 @@ impl str { /// ``` #[stable(feature = "repeat_str", since = "1.16.0")] pub fn repeat(&self, n: usize) -> String { - let mut s = String::with_capacity(self.len() * n); - s.extend((0..n).map(|_| self)); - s + if n == 0 { + return String::new(); + } + + // n = 2^j + k (2^j > k) + + // 2^j: + let mut s = Vec::with_capacity(self.len() * n); + s.extend(self.as_bytes()); + let mut m = n >> 1; + while m > 0 { + let len = s.len(); + unsafe { + ptr::copy_nonoverlapping(s.as_ptr(), (s.as_mut_ptr() as *mut u8).add(len), len); + s.set_len(len * 2); + } + m >>= 1; + } + + // k: + let res_len = n * self.len(); + if res_len > s.len() { + unsafe { + ptr::copy_nonoverlapping( + s.as_ptr(), + (s.as_mut_ptr() as *mut u8).add(s.len()), + res_len - s.len(), + ); + s.set_len(res_len); + } + } + + unsafe { String::from_utf8_unchecked(s) } } /// Checks if all characters in this string are within the ASCII range. -- cgit 1.4.1-3-g733a5 From 46fef766b0dc3717937911f0f67c9f1ee2a7f21e Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Sun, 28 Jan 2018 14:24:19 -0500 Subject: Have Vec use slice's implementations of Index and IndexMut --- src/liballoc/vec.rs | 130 ++-------------------------------------------------- 1 file changed, 5 insertions(+), 125 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 3c9b6b94b44..9c6b10c32f1 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1527,11 +1527,11 @@ impl Hash for Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl Index for Vec { - type Output = T; +impl Index for Vec where [T]: Index { + type Output = <[T] as Index>::Output; #[inline] - fn index(&self, index: usize) -> &T { + fn index(&self, index: I) -> &Self::Output { // NB built-in indexing via `&[T]` &(**self)[index] } @@ -1539,134 +1539,14 @@ impl Index for Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl IndexMut for Vec { +impl IndexMut for Vec where [T]: IndexMut { #[inline] - fn index_mut(&mut self, index: usize) -> &mut T { + fn index_mut(&mut self, index: I) -> &mut Self::Output { // NB built-in indexing via `&mut [T]` &mut (**self)[index] } } -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index> for Vec { - type Output = [T]; - - #[inline] - fn index(&self, index: ops::Range) -> &[T] { - Index::index(&**self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index> for Vec { - type Output = [T]; - - #[inline] - fn index(&self, index: ops::RangeTo) -> &[T] { - Index::index(&**self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index> for Vec { - type Output = [T]; - - #[inline] - fn index(&self, index: ops::RangeFrom) -> &[T] { - Index::index(&**self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index for Vec { - type Output = [T]; - - #[inline] - fn index(&self, _index: ops::RangeFull) -> &[T] { - self - } -} - -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index> for Vec { - type Output = [T]; - - #[inline] - fn index(&self, index: ops::RangeInclusive) -> &[T] { - Index::index(&**self, index) - } -} - -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::Index> for Vec { - type Output = [T]; - - #[inline] - fn index(&self, index: ops::RangeToInclusive) -> &[T] { - Index::index(&**self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut> for Vec { - #[inline] - fn index_mut(&mut self, index: ops::Range) -> &mut [T] { - IndexMut::index_mut(&mut **self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut> for Vec { - #[inline] - fn index_mut(&mut self, index: ops::RangeTo) -> &mut [T] { - IndexMut::index_mut(&mut **self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut> for Vec { - #[inline] - fn index_mut(&mut self, index: ops::RangeFrom) -> &mut [T] { - IndexMut::index_mut(&mut **self, index) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut for Vec { - #[inline] - fn index_mut(&mut self, _index: ops::RangeFull) -> &mut [T] { - self - } -} - -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut> for Vec { - #[inline] - fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut [T] { - IndexMut::index_mut(&mut **self, index) - } -} - -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl ops::IndexMut> for Vec { - #[inline] - fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut [T] { - IndexMut::index_mut(&mut **self, index) - } -} - #[stable(feature = "rust1", since = "1.0.0")] impl ops::Deref for Vec { type Target = [T]; -- cgit 1.4.1-3-g733a5 From 45bdf9cbeec3788c29d8958dc661dd449bb7247c Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Mon, 29 Jan 2018 15:02:44 -0500 Subject: Update comments --- src/liballoc/vec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 9c6b10c32f1..83538e5acd4 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1532,7 +1532,7 @@ impl Index for Vec where [T]: Index { #[inline] fn index(&self, index: I) -> &Self::Output { - // NB built-in indexing via `&[T]` + // NB indexing via implementation on slice &(**self)[index] } } @@ -1542,7 +1542,7 @@ impl Index for Vec where [T]: Index { impl IndexMut for Vec where [T]: IndexMut { #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { - // NB built-in indexing via `&mut [T]` + // NB indexing via implementation on slice &mut (**self)[index] } } -- cgit 1.4.1-3-g733a5 From 370df40dab8df9f3c0b10bb7396225b8d24869b3 Mon Sep 17 00:00:00 2001 From: Jonathan Behrens Date: Fri, 2 Mar 2018 23:23:00 -0500 Subject: Don't have Vec delegate to [T]'s bounds for indexing --- src/liballoc/vec.rs | 18 +++++++++++------- src/test/ui/index-help.stderr | 2 +- 2 files changed, 12 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 83538e5acd4..feed7c8699a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1527,23 +1527,27 @@ impl Hash for Vec { #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl Index for Vec where [T]: Index { - type Output = <[T] as Index>::Output; +impl Index for Vec +where + I: ::core::slice::SliceIndex<[T]>, +{ + type Output = I::Output; #[inline] fn index(&self, index: I) -> &Self::Output { - // NB indexing via implementation on slice - &(**self)[index] + Index::index(&**self, index) } } #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] -impl IndexMut for Vec where [T]: IndexMut { +impl IndexMut for Vec +where + I: ::core::slice::SliceIndex<[T]>, +{ #[inline] fn index_mut(&mut self, index: I) -> &mut Self::Output { - // NB indexing via implementation on slice - &mut (**self)[index] + IndexMut::index_mut(&mut **self, index) } } diff --git a/src/test/ui/index-help.stderr b/src/test/ui/index-help.stderr index 1b5d28d5bb1..ae3cd529ac4 100644 --- a/src/test/ui/index-help.stderr +++ b/src/test/ui/index-help.stderr @@ -5,7 +5,7 @@ LL | x[0i32]; //~ ERROR E0277 | ^^^^^^^ slice indices are of type `usize` or ranges of `usize` | = help: the trait `std::slice::SliceIndex<[{integer}]>` is not implemented for `i32` - = note: required because of the requirements on the impl of `std::ops::Index` for `[{integer}]` + = note: required because of the requirements on the impl of `std::ops::Index` for `std::vec::Vec<{integer}>` error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From bc651cac8d671aee9be876b71d0fa86f94f56b0f Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Mon, 15 Jan 2018 19:59:10 +0100 Subject: core: Stabilize FusedIterator FusedIterator is a marker trait that promises that the implementing iterator continues to return `None` from `.next()` once it has returned `None` once (and/or `.next_back()`, if implemented). The effects of FusedIterator are already widely available through `.fuse()`, but with stable `FusedIterator`, stable Rust users can implement this trait for their iterators when appropriate. --- src/liballoc/binary_heap.rs | 6 +++--- src/liballoc/boxed.rs | 2 +- src/liballoc/btree/map.rs | 16 +++++++-------- src/liballoc/btree/set.rs | 14 ++++++------- src/liballoc/lib.rs | 3 +-- src/liballoc/linked_list.rs | 6 +++--- src/liballoc/str.rs | 2 +- src/liballoc/string.rs | 2 +- src/liballoc/vec.rs | 4 ++-- src/liballoc/vec_deque.rs | 8 ++++---- src/libcore/char.rs | 8 ++++---- src/libcore/iter/mod.rs | 42 +++++++++++++++++++------------------- src/libcore/iter/range.rs | 6 +++--- src/libcore/iter/sources.rs | 6 +++--- src/libcore/iter/traits.rs | 4 ++-- src/libcore/option.rs | 6 +++--- src/libcore/result.rs | 6 +++--- src/libcore/slice/mod.rs | 24 +++++++++++----------- src/libcore/str/mod.rs | 14 ++++++------- src/libstd/ascii.rs | 2 +- src/libstd/collections/hash/map.rs | 14 ++++++------- src/libstd/collections/hash/set.rs | 14 ++++++------- src/libstd/lib.rs | 1 - src/libstd/path.rs | 4 ++-- src/libstd_unicode/char.rs | 4 ++-- src/libstd_unicode/lib.rs | 1 - src/libstd_unicode/u_str.rs | 4 ++-- src/test/run-pass/issue-36053.rs | 1 - 28 files changed, 110 insertions(+), 114 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index 3041f85cd4c..a5694a90dbc 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -964,7 +964,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} /// An owning iterator over the elements of a `BinaryHeap`. @@ -1019,7 +1019,7 @@ impl ExactSizeIterator for IntoIter { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} /// A draining iterator over the elements of a `BinaryHeap`. @@ -1065,7 +1065,7 @@ impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 75a59de337c..6b000b6fa91 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -722,7 +722,7 @@ impl ExactSizeIterator for Box { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Box {} diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 618ef81fdd9..7b7a6374db9 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -1156,7 +1156,7 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1235,7 +1235,7 @@ impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1365,7 +1365,7 @@ impl ExactSizeIterator for IntoIter { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1395,7 +1395,7 @@ impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1432,7 +1432,7 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Values<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1482,7 +1482,7 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} @@ -1561,7 +1561,7 @@ impl<'a, K, V> Range<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Range<'a, K, V> {} #[stable(feature = "btree_range", since = "1.17.0")] @@ -1630,7 +1630,7 @@ impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for RangeMut<'a, K, V> {} impl<'a, K, V> RangeMut<'a, K, V> { diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs index 327eaaf4651..34cb7a08ed7 100644 --- a/src/liballoc/btree/set.rs +++ b/src/liballoc/btree/set.rs @@ -946,7 +946,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { fn len(&self) -> usize { self.iter.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -971,7 +971,7 @@ impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.iter.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[stable(feature = "btree_range", since = "1.17.0")] @@ -997,7 +997,7 @@ impl<'a, T> DoubleEndedIterator for Range<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Range<'a, T> {} /// Compare `x` and `y`, but return `short` if x is None and `long` if y is None @@ -1044,7 +1044,7 @@ impl<'a, T: Ord> Iterator for Difference<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: Ord> FusedIterator for Difference<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1078,7 +1078,7 @@ impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: Ord> FusedIterator for SymmetricDifference<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1116,7 +1116,7 @@ impl<'a, T: Ord> Iterator for Intersection<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: Ord> FusedIterator for Intersection<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1150,5 +1150,5 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: Ord> FusedIterator for Union<'a, T> {} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d250cfe1880..2212b62dfd8 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -96,7 +96,6 @@ #![feature(fmt_internals)] #![feature(from_ref)] #![feature(fundamental)] -#![feature(fused)] #![feature(generic_param_attrs)] #![feature(i128_type)] #![feature(inclusive_range)] @@ -125,7 +124,7 @@ #![feature(on_unimplemented)] #![feature(exact_chunks)] -#![cfg_attr(not(test), feature(fused, fn_traits, placement_new_protocol, swap_with_slice, i128))] +#![cfg_attr(not(test), feature(fn_traits, placement_new_protocol, swap_with_slice, i128))] #![cfg_attr(test, feature(test, box_heap))] // Allow testing this library diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index ec579e3fd68..87939fddfc8 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -897,7 +897,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Iter<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -946,7 +946,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} impl<'a, T> IterMut<'a, T> { @@ -1117,7 +1117,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index a00e3d17dd0..1a431bb2694 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -171,7 +171,7 @@ impl<'a> Iterator for EncodeUtf16<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for EncodeUtf16<'a> {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 409d2ab287e..1fcabd8a427 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2254,5 +2254,5 @@ impl<'a> DoubleEndedIterator for Drain<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Drain<'a> {} diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 3c9b6b94b44..03c9750fb39 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2389,7 +2389,7 @@ impl ExactSizeIterator for IntoIter { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -2495,7 +2495,7 @@ impl<'a, T> ExactSizeIterator for Drain<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Drain<'a, T> {} /// A place for insertion at the back of a `Vec`. diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 8b686365e69..3d7549abe6f 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -1991,7 +1991,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} @@ -2084,7 +2084,7 @@ impl<'a, T> ExactSizeIterator for IterMut<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} /// An owning iterator over the elements of a `VecDeque`. @@ -2140,7 +2140,7 @@ impl ExactSizeIterator for IntoIter { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} /// A draining iterator over the elements of a `VecDeque`. @@ -2247,7 +2247,7 @@ impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { #[stable(feature = "drain", since = "1.6.0")] impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 7215bd2a476..3ff31a7a928 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -643,7 +643,7 @@ impl ExactSizeIterator for EscapeUnicode { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for EscapeUnicode {} #[stable(feature = "char_struct_display", since = "1.16.0")] @@ -756,7 +756,7 @@ impl ExactSizeIterator for EscapeDefault { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for EscapeDefault {} #[stable(feature = "char_struct_display", since = "1.16.0")] @@ -790,7 +790,7 @@ impl Iterator for EscapeDebug { #[stable(feature = "char_escape_debug", since = "1.20.0")] impl ExactSizeIterator for EscapeDebug { } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for EscapeDebug {} #[stable(feature = "char_escape_debug", since = "1.20.0")] @@ -904,5 +904,5 @@ impl> Iterator for DecodeUtf8 { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl> FusedIterator for DecodeUtf8 {} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 257d7d6caaa..9a8f7fc4e6a 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -344,7 +344,7 @@ pub use self::sources::{Once, once}; pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{ExactSizeIterator, Sum, Product}; -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] pub use self::traits::FusedIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; @@ -506,7 +506,7 @@ impl ExactSizeIterator for Rev } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Rev where I: FusedIterator + DoubleEndedIterator {} @@ -589,7 +589,7 @@ impl<'a, I, T: 'a> ExactSizeIterator for Cloned } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, I, T: 'a> FusedIterator for Cloned where I: FusedIterator, T: Clone {} @@ -662,7 +662,7 @@ impl Iterator for Cycle where I: Clone + Iterator { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Cycle where I: Clone + Iterator {} /// An iterator for stepping iterators by a custom amount. @@ -1002,7 +1002,7 @@ impl DoubleEndedIterator for Chain where } // Note: *both* must be fused to handle double-ended iterators. -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Chain where A: FusedIterator, B: FusedIterator, @@ -1262,7 +1262,7 @@ unsafe impl TrustedRandomAccess for Zip } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Zip where A: FusedIterator, B: FusedIterator, {} @@ -1404,7 +1404,7 @@ impl ExactSizeIterator for Map } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Map where F: FnMut(I::Item) -> B {} @@ -1553,7 +1553,7 @@ impl DoubleEndedIterator for Filter } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Filter where P: FnMut(&I::Item) -> bool {} @@ -1663,7 +1663,7 @@ impl DoubleEndedIterator for FilterMap } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for FilterMap where F: FnMut(I::Item) -> Option {} @@ -1818,7 +1818,7 @@ unsafe impl TrustedRandomAccess for Enumerate } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Enumerate where I: FusedIterator {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1938,7 +1938,7 @@ impl Iterator for Peekable { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Peekable {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Peekable {} impl Peekable { @@ -2072,7 +2072,7 @@ impl Iterator for SkipWhile } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for SkipWhile where I: FusedIterator, P: FnMut(&I::Item) -> bool {} @@ -2151,7 +2151,7 @@ impl Iterator for TakeWhile } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for TakeWhile where I: FusedIterator, P: FnMut(&I::Item) -> bool {} @@ -2290,7 +2290,7 @@ impl DoubleEndedIterator for Skip where I: DoubleEndedIterator + ExactSize } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Skip where I: FusedIterator {} /// An iterator that only iterates over the first `n` iterations of `iter`. @@ -2371,7 +2371,7 @@ impl Iterator for Take where I: Iterator{ #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Take where I: ExactSizeIterator {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Take where I: FusedIterator {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -2517,7 +2517,7 @@ impl DoubleEndedIterator for FlatMap } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for FlatMap where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} @@ -2605,7 +2605,7 @@ impl DoubleEndedIterator for Flatten } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Flatten where I: FusedIterator, U: Iterator, I::Item: IntoIterator {} @@ -2765,7 +2765,7 @@ pub struct Fuse { done: bool } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Fuse where I: Iterator {} #[stable(feature = "rust1", since = "1.0.0")] @@ -2896,7 +2896,7 @@ unsafe impl TrustedRandomAccess for Fuse } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl Iterator for Fuse where I: FusedIterator { #[inline] fn next(&mut self) -> Option<::Item> { @@ -2938,7 +2938,7 @@ impl Iterator for Fuse where I: FusedIterator { } } -#[unstable(feature = "fused", reason = "recently added", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl DoubleEndedIterator for Fuse where I: DoubleEndedIterator + FusedIterator { @@ -3082,6 +3082,6 @@ impl ExactSizeIterator for Inspect } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Inspect where F: FnMut(&I::Item) {} diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 65b38c94dda..7f3b227e8b7 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -295,7 +295,7 @@ impl DoubleEndedIterator for ops::Range { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for ops::Range {} #[stable(feature = "rust1", since = "1.0.0")] @@ -322,7 +322,7 @@ impl Iterator for ops::RangeFrom { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for ops::RangeFrom {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -463,5 +463,5 @@ impl DoubleEndedIterator for ops::RangeInclusive { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for ops::RangeInclusive {} diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index dfd42f3e733..149dff83bc0 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -41,7 +41,7 @@ impl DoubleEndedIterator for Repeat { fn next_back(&mut self) -> Option { Some(self.element.clone()) } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Repeat {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -259,7 +259,7 @@ impl ExactSizeIterator for Empty { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Empty {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Empty {} // not #[derive] because that adds a Clone bound on T, @@ -340,7 +340,7 @@ impl ExactSizeIterator for Once { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Once {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Once {} /// Creates an iterator that yields an element exactly once. diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 860742d9eab..a86f4e74706 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -959,10 +959,10 @@ impl Product> for Result /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`Iterator::fuse`]: ../../std/iter/trait.Iterator.html#method.fuse /// [`Fuse`]: ../../std/iter/struct.Fuse.html -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] pub trait FusedIterator: Iterator {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, I: FusedIterator + ?Sized> FusedIterator for &'a mut I {} /// An iterator that reports an accurate length using size_hint. diff --git a/src/libcore/option.rs b/src/libcore/option.rs index b8fe28d0f0d..99c1d7d9b36 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -1051,7 +1051,7 @@ impl<'a, A> DoubleEndedIterator for Iter<'a, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, A> ExactSizeIterator for Iter<'a, A> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, A> FusedIterator for Iter<'a, A> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1096,7 +1096,7 @@ impl<'a, A> DoubleEndedIterator for IterMut<'a, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, A> ExactSizeIterator for IterMut<'a, A> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, A> FusedIterator for IterMut<'a, A> {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl<'a, A> TrustedLen for IterMut<'a, A> {} @@ -1133,7 +1133,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 3801db94e15..5131fc837ef 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1038,7 +1038,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Iter<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1082,7 +1082,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1125,7 +1125,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index a43ed65907f..02207f1738f 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1461,7 +1461,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1589,7 +1589,7 @@ impl<'a, T> ExactSizeIterator for IterMut<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1737,7 +1737,7 @@ impl<'a, T, P> SplitIter for Split<'a, T, P> where P: FnMut(&T) -> bool { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, P> FusedIterator for Split<'a, T, P> where P: FnMut(&T) -> bool {} /// An iterator over the subslices of the vector which are separated @@ -1835,7 +1835,7 @@ impl<'a, T, P> DoubleEndedIterator for SplitMut<'a, T, P> where } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, P> FusedIterator for SplitMut<'a, T, P> where P: FnMut(&T) -> bool {} /// An iterator over subslices separated by elements that match a predicate @@ -1892,7 +1892,7 @@ impl<'a, T, P> SplitIter for RSplit<'a, T, P> where P: FnMut(&T) -> bool { } } -//#[unstable(feature = "fused", issue = "35602")] +//#[stable(feature = "fused", since = "1.25.0")] #[unstable(feature = "slice_rsplit", issue = "41020")] impl<'a, T, P> FusedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool {} @@ -1951,7 +1951,7 @@ impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> where } } -//#[unstable(feature = "fused", issue = "35602")] +//#[stable(feature = "fused", since = "1.25.0")] #[unstable(feature = "slice_rsplit", issue = "41020")] impl<'a, T, P> FusedIterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool {} @@ -2088,7 +2088,7 @@ macro_rules! forward_iterator { } } - #[unstable(feature = "fused", issue = "35602")] + #[stable(feature = "fused", since = "1.25.0")] impl<'a, $elem, P> FusedIterator for $name<'a, $elem, P> where P: FnMut(&T) -> bool {} } @@ -2194,7 +2194,7 @@ impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Windows<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Windows<'a, T> {} #[doc(hidden)] @@ -2313,7 +2313,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Chunks<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for Chunks<'a, T> {} #[doc(hidden)] @@ -2429,7 +2429,7 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for ChunksMut<'a, T> {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for ChunksMut<'a, T> {} #[doc(hidden)] @@ -2539,7 +2539,7 @@ impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for ExactChunks<'a, T> {} #[doc(hidden)] @@ -2636,7 +2636,7 @@ impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} #[doc(hidden)] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 765b369e4b2..7e919b653f2 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -609,7 +609,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Chars<'a> {} impl<'a> Chars<'a> { @@ -702,7 +702,7 @@ impl<'a> DoubleEndedIterator for CharIndices<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for CharIndices<'a> {} impl<'a> CharIndices<'a> { @@ -817,7 +817,7 @@ impl<'a> ExactSizeIterator for Bytes<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Bytes<'a> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -977,10 +977,10 @@ macro_rules! generate_pattern_iterators { } } - #[unstable(feature = "fused", issue = "35602")] + #[stable(feature = "fused", since = "1.25.0")] impl<'a, P: Pattern<'a>> FusedIterator for $forward_iterator<'a, P> {} - #[unstable(feature = "fused", issue = "35602")] + #[stable(feature = "fused", since = "1.25.0")] impl<'a, P: Pattern<'a>> FusedIterator for $reverse_iterator<'a, P> where P::Searcher: ReverseSearcher<'a> {} @@ -1337,7 +1337,7 @@ impl<'a> DoubleEndedIterator for Lines<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Lines<'a> {} /// Created with the method [`lines_any`]. @@ -1403,7 +1403,7 @@ impl<'a> DoubleEndedIterator for LinesAny<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] #[allow(deprecated)] impl<'a> FusedIterator for LinesAny<'a> {} diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index 430c9df396a..ce3e38de7eb 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -590,7 +590,7 @@ impl DoubleEndedIterator for EscapeDefault { } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for EscapeDefault {} -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for EscapeDefault {} #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 4dfdc23ebee..9e48efeb113 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1750,7 +1750,7 @@ impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1773,7 +1773,7 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1808,7 +1808,7 @@ impl ExactSizeIterator for IntoIter { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1840,7 +1840,7 @@ impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1863,7 +1863,7 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Values<'a, K, V> {} #[stable(feature = "map_values_mut", since = "1.10.0")] @@ -1886,7 +1886,7 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1921,7 +1921,7 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { self.inner.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index e9427fb40a0..7a46603b2db 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -1097,7 +1097,7 @@ impl<'a, K> ExactSizeIterator for Iter<'a, K> { self.iter.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K> FusedIterator for Iter<'a, K> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1124,7 +1124,7 @@ impl ExactSizeIterator for IntoIter { self.iter.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1155,7 +1155,7 @@ impl<'a, K> ExactSizeIterator for Drain<'a, K> { self.iter.len() } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, K> FusedIterator for Drain<'a, K> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1208,7 +1208,7 @@ impl<'a, T, S> fmt::Debug for Intersection<'a, T, S> } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, S> FusedIterator for Intersection<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1244,7 +1244,7 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, S> FusedIterator for Difference<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1283,7 +1283,7 @@ impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, S> FusedIterator for SymmetricDifference<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1307,7 +1307,7 @@ impl<'a, T, S> Clone for Union<'a, T, S> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a, T, S> FusedIterator for Union<'a, T, S> where T: Eq + Hash, S: BuildHasher diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d7d856fe3ad..82c4443a45e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -266,7 +266,6 @@ #![feature(float_from_str_radix)] #![feature(fn_traits)] #![feature(fnbox)] -#![feature(fused)] #![feature(generic_param_attrs)] #![feature(hashmap_hasher)] #![feature(heap_api)] diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 1608a752a46..0c54fb00d2d 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -905,7 +905,7 @@ impl<'a> DoubleEndedIterator for Iter<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Iter<'a> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1008,7 +1008,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for Components<'a> {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index 844ff7a3c12..2bbbf6c0655 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -70,7 +70,7 @@ impl Iterator for ToLowercase { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for ToLowercase {} /// Returns an iterator that yields the uppercase equivalent of a `char`. @@ -92,7 +92,7 @@ impl Iterator for ToUppercase { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for ToUppercase {} #[derive(Debug)] diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index dcae7d0af40..f155b62e3cc 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -36,7 +36,6 @@ #![feature(str_internals)] #![feature(decode_utf8)] #![feature(fn_traits)] -#![feature(fused)] #![feature(lang_items)] #![feature(non_exhaustive)] #![feature(staged_api)] diff --git a/src/libstd_unicode/u_str.rs b/src/libstd_unicode/u_str.rs index 5d1611acb7e..ed2f205b580 100644 --- a/src/libstd_unicode/u_str.rs +++ b/src/libstd_unicode/u_str.rs @@ -127,7 +127,7 @@ impl Iterator for Utf16Encoder } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Utf16Encoder where I: FusedIterator {} @@ -186,5 +186,5 @@ impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[stable(feature = "fused", since = "1.25.0")] impl<'a> FusedIterator for SplitWhitespace<'a> {} diff --git a/src/test/run-pass/issue-36053.rs b/src/test/run-pass/issue-36053.rs index 2411996cf05..ece58eedc56 100644 --- a/src/test/run-pass/issue-36053.rs +++ b/src/test/run-pass/issue-36053.rs @@ -14,7 +14,6 @@ // `FusedIterator` in std but I was not able to isolate that into an // external crate. -#![feature(fused)] use std::iter::FusedIterator; struct Thing<'a>(&'a str); -- cgit 1.4.1-3-g733a5 From c7c23fe9482c129855a667909ff58969f6efe1f6 Mon Sep 17 00:00:00 2001 From: Ulrik Sverdrup Date: Sat, 3 Mar 2018 14:15:28 +0100 Subject: core: Update stability attributes for FusedIterator --- src/liballoc/binary_heap.rs | 6 +++--- src/liballoc/boxed.rs | 2 +- src/liballoc/btree/map.rs | 16 +++++++-------- src/liballoc/btree/set.rs | 14 ++++++------- src/liballoc/linked_list.rs | 6 +++--- src/liballoc/str.rs | 2 +- src/liballoc/string.rs | 2 +- src/liballoc/vec.rs | 4 ++-- src/liballoc/vec_deque.rs | 8 ++++---- src/libcore/char.rs | 8 ++++---- src/libcore/iter/mod.rs | 42 +++++++++++++++++++------------------- src/libcore/iter/range.rs | 6 +++--- src/libcore/iter/sources.rs | 8 ++++---- src/libcore/iter/traits.rs | 4 ++-- src/libcore/option.rs | 6 +++--- src/libcore/result.rs | 6 +++--- src/libcore/slice/mod.rs | 22 +++++++++----------- src/libcore/str/mod.rs | 14 ++++++------- src/libstd/ascii.rs | 2 +- src/libstd/collections/hash/map.rs | 14 ++++++------- src/libstd/collections/hash/set.rs | 14 ++++++------- src/libstd/path.rs | 6 +++--- src/libstd_unicode/char.rs | 4 ++-- src/libstd_unicode/u_str.rs | 3 +-- 24 files changed, 108 insertions(+), 111 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index a5694a90dbc..8aaac5d6e08 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -964,7 +964,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} /// An owning iterator over the elements of a `BinaryHeap`. @@ -1019,7 +1019,7 @@ impl ExactSizeIterator for IntoIter { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} /// A draining iterator over the elements of a `BinaryHeap`. @@ -1065,7 +1065,7 @@ impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6b000b6fa91..b776556d59f 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -722,7 +722,7 @@ impl ExactSizeIterator for Box { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Box {} diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 7b7a6374db9..ed9c8c18f0d 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -1156,7 +1156,7 @@ impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1235,7 +1235,7 @@ impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1365,7 +1365,7 @@ impl ExactSizeIterator for IntoIter { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1395,7 +1395,7 @@ impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1432,7 +1432,7 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Values<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1482,7 +1482,7 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} @@ -1561,7 +1561,7 @@ impl<'a, K, V> Range<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Range<'a, K, V> {} #[stable(feature = "btree_range", since = "1.17.0")] @@ -1630,7 +1630,7 @@ impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for RangeMut<'a, K, V> {} impl<'a, K, V> RangeMut<'a, K, V> { diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs index 34cb7a08ed7..2e3157147a0 100644 --- a/src/liballoc/btree/set.rs +++ b/src/liballoc/btree/set.rs @@ -946,7 +946,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { fn len(&self) -> usize { self.iter.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -971,7 +971,7 @@ impl ExactSizeIterator for IntoIter { fn len(&self) -> usize { self.iter.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[stable(feature = "btree_range", since = "1.17.0")] @@ -997,7 +997,7 @@ impl<'a, T> DoubleEndedIterator for Range<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Range<'a, T> {} /// Compare `x` and `y`, but return `short` if x is None and `long` if y is None @@ -1044,7 +1044,7 @@ impl<'a, T: Ord> Iterator for Difference<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: Ord> FusedIterator for Difference<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1078,7 +1078,7 @@ impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: Ord> FusedIterator for SymmetricDifference<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1116,7 +1116,7 @@ impl<'a, T: Ord> Iterator for Intersection<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: Ord> FusedIterator for Intersection<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1150,5 +1150,5 @@ impl<'a, T: Ord> Iterator for Union<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: Ord> FusedIterator for Union<'a, T> {} diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 87939fddfc8..097d2e414f5 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -897,7 +897,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Iter<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -946,7 +946,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} impl<'a, T> IterMut<'a, T> { @@ -1117,7 +1117,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 1a431bb2694..82f8476d670 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -171,7 +171,7 @@ impl<'a> Iterator for EncodeUtf16<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for EncodeUtf16<'a> {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 1fcabd8a427..370fb6b4e89 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2254,5 +2254,5 @@ impl<'a> DoubleEndedIterator for Drain<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Drain<'a> {} diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 03c9750fb39..9e5672ef148 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2389,7 +2389,7 @@ impl ExactSizeIterator for IntoIter { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -2495,7 +2495,7 @@ impl<'a, T> ExactSizeIterator for Drain<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Drain<'a, T> {} /// A place for insertion at the back of a `Vec`. diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 3d7549abe6f..68add3cbd51 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -1991,7 +1991,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} @@ -2084,7 +2084,7 @@ impl<'a, T> ExactSizeIterator for IterMut<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} /// An owning iterator over the elements of a `VecDeque`. @@ -2140,7 +2140,7 @@ impl ExactSizeIterator for IntoIter { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} /// A draining iterator over the elements of a `VecDeque`. @@ -2247,7 +2247,7 @@ impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { #[stable(feature = "drain", since = "1.6.0")] impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/char.rs b/src/libcore/char.rs index 3ff31a7a928..c90cca7dbe4 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -643,7 +643,7 @@ impl ExactSizeIterator for EscapeUnicode { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for EscapeUnicode {} #[stable(feature = "char_struct_display", since = "1.16.0")] @@ -756,7 +756,7 @@ impl ExactSizeIterator for EscapeDefault { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for EscapeDefault {} #[stable(feature = "char_struct_display", since = "1.16.0")] @@ -790,7 +790,7 @@ impl Iterator for EscapeDebug { #[stable(feature = "char_escape_debug", since = "1.20.0")] impl ExactSizeIterator for EscapeDebug { } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for EscapeDebug {} #[stable(feature = "char_escape_debug", since = "1.20.0")] @@ -904,5 +904,5 @@ impl> Iterator for DecodeUtf8 { } } -#[stable(feature = "fused", since = "1.25.0")] +#[unstable(feature = "decode_utf8", issue = "33906")] impl> FusedIterator for DecodeUtf8 {} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 9a8f7fc4e6a..a6802d606ca 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -344,7 +344,7 @@ pub use self::sources::{Once, once}; pub use self::traits::{FromIterator, IntoIterator, DoubleEndedIterator, Extend}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::traits::{ExactSizeIterator, Sum, Product}; -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] pub use self::traits::FusedIterator; #[unstable(feature = "trusted_len", issue = "37572")] pub use self::traits::TrustedLen; @@ -506,7 +506,7 @@ impl ExactSizeIterator for Rev } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Rev where I: FusedIterator + DoubleEndedIterator {} @@ -589,7 +589,7 @@ impl<'a, I, T: 'a> ExactSizeIterator for Cloned } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, I, T: 'a> FusedIterator for Cloned where I: FusedIterator, T: Clone {} @@ -662,7 +662,7 @@ impl Iterator for Cycle where I: Clone + Iterator { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Cycle where I: Clone + Iterator {} /// An iterator for stepping iterators by a custom amount. @@ -1002,7 +1002,7 @@ impl DoubleEndedIterator for Chain where } // Note: *both* must be fused to handle double-ended iterators. -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Chain where A: FusedIterator, B: FusedIterator, @@ -1262,7 +1262,7 @@ unsafe impl TrustedRandomAccess for Zip } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Zip where A: FusedIterator, B: FusedIterator, {} @@ -1404,7 +1404,7 @@ impl ExactSizeIterator for Map } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Map where F: FnMut(I::Item) -> B {} @@ -1553,7 +1553,7 @@ impl DoubleEndedIterator for Filter } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Filter where P: FnMut(&I::Item) -> bool {} @@ -1663,7 +1663,7 @@ impl DoubleEndedIterator for FilterMap } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for FilterMap where F: FnMut(I::Item) -> Option {} @@ -1818,7 +1818,7 @@ unsafe impl TrustedRandomAccess for Enumerate } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Enumerate where I: FusedIterator {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1938,7 +1938,7 @@ impl Iterator for Peekable { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Peekable {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Peekable {} impl Peekable { @@ -2072,7 +2072,7 @@ impl Iterator for SkipWhile } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for SkipWhile where I: FusedIterator, P: FnMut(&I::Item) -> bool {} @@ -2151,7 +2151,7 @@ impl Iterator for TakeWhile } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for TakeWhile where I: FusedIterator, P: FnMut(&I::Item) -> bool {} @@ -2290,7 +2290,7 @@ impl DoubleEndedIterator for Skip where I: DoubleEndedIterator + ExactSize } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Skip where I: FusedIterator {} /// An iterator that only iterates over the first `n` iterations of `iter`. @@ -2371,7 +2371,7 @@ impl Iterator for Take where I: Iterator{ #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for Take where I: ExactSizeIterator {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Take where I: FusedIterator {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -2517,7 +2517,7 @@ impl DoubleEndedIterator for FlatMap } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for FlatMap where I: FusedIterator, U: IntoIterator, F: FnMut(I::Item) -> U {} @@ -2605,7 +2605,7 @@ impl DoubleEndedIterator for Flatten } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Flatten where I: FusedIterator, U: Iterator, I::Item: IntoIterator {} @@ -2765,7 +2765,7 @@ pub struct Fuse { done: bool } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Fuse where I: Iterator {} #[stable(feature = "rust1", since = "1.0.0")] @@ -2896,7 +2896,7 @@ unsafe impl TrustedRandomAccess for Fuse } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl Iterator for Fuse where I: FusedIterator { #[inline] fn next(&mut self) -> Option<::Item> { @@ -2938,7 +2938,7 @@ impl Iterator for Fuse where I: FusedIterator { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl DoubleEndedIterator for Fuse where I: DoubleEndedIterator + FusedIterator { @@ -3082,6 +3082,6 @@ impl ExactSizeIterator for Inspect } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Inspect where F: FnMut(&I::Item) {} diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 7f3b227e8b7..9a3fd215dcf 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -295,7 +295,7 @@ impl DoubleEndedIterator for ops::Range { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ops::Range {} #[stable(feature = "rust1", since = "1.0.0")] @@ -322,7 +322,7 @@ impl Iterator for ops::RangeFrom { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ops::RangeFrom {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -463,5 +463,5 @@ impl DoubleEndedIterator for ops::RangeInclusive { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ops::RangeInclusive {} diff --git a/src/libcore/iter/sources.rs b/src/libcore/iter/sources.rs index 149dff83bc0..0fc1a3aa8ac 100644 --- a/src/libcore/iter/sources.rs +++ b/src/libcore/iter/sources.rs @@ -41,7 +41,7 @@ impl DoubleEndedIterator for Repeat { fn next_back(&mut self) -> Option { Some(self.element.clone()) } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Repeat {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -135,7 +135,7 @@ impl A> DoubleEndedIterator for RepeatWith { fn next_back(&mut self) -> Option { self.next() } } -#[unstable(feature = "fused", issue = "35602")] +#[unstable(feature = "iterator_repeat_with", issue = "48169")] impl A> FusedIterator for RepeatWith {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -259,7 +259,7 @@ impl ExactSizeIterator for Empty { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Empty {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Empty {} // not #[derive] because that adds a Clone bound on T, @@ -340,7 +340,7 @@ impl ExactSizeIterator for Once { #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for Once {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for Once {} /// Creates an iterator that yields an element exactly once. diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index a86f4e74706..0267fcd3754 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -959,10 +959,10 @@ impl Product> for Result /// [`None`]: ../../std/option/enum.Option.html#variant.None /// [`Iterator::fuse`]: ../../std/iter/trait.Iterator.html#method.fuse /// [`Fuse`]: ../../std/iter/struct.Fuse.html -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] pub trait FusedIterator: Iterator {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, I: FusedIterator + ?Sized> FusedIterator for &'a mut I {} /// An iterator that reports an accurate length using size_hint. diff --git a/src/libcore/option.rs b/src/libcore/option.rs index 99c1d7d9b36..b66aad246fe 100644 --- a/src/libcore/option.rs +++ b/src/libcore/option.rs @@ -1051,7 +1051,7 @@ impl<'a, A> DoubleEndedIterator for Iter<'a, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, A> ExactSizeIterator for Iter<'a, A> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, A> FusedIterator for Iter<'a, A> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1096,7 +1096,7 @@ impl<'a, A> DoubleEndedIterator for IterMut<'a, A> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, A> ExactSizeIterator for IterMut<'a, A> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, A> FusedIterator for IterMut<'a, A> {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl<'a, A> TrustedLen for IterMut<'a, A> {} @@ -1133,7 +1133,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/src/libcore/result.rs b/src/libcore/result.rs index 5131fc837ef..c152d4979b9 100644 --- a/src/libcore/result.rs +++ b/src/libcore/result.rs @@ -1038,7 +1038,7 @@ impl<'a, T> DoubleEndedIterator for Iter<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Iter<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1082,7 +1082,7 @@ impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1125,7 +1125,7 @@ impl DoubleEndedIterator for IntoIter { #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for IntoIter {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[unstable(feature = "trusted_len", issue = "37572")] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 02207f1738f..4f1ee2d9491 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1461,7 +1461,7 @@ impl<'a, T> ExactSizeIterator for Iter<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Iter<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1589,7 +1589,7 @@ impl<'a, T> ExactSizeIterator for IterMut<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for IterMut<'a, T> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -1737,7 +1737,7 @@ impl<'a, T, P> SplitIter for Split<'a, T, P> where P: FnMut(&T) -> bool { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, P> FusedIterator for Split<'a, T, P> where P: FnMut(&T) -> bool {} /// An iterator over the subslices of the vector which are separated @@ -1835,7 +1835,7 @@ impl<'a, T, P> DoubleEndedIterator for SplitMut<'a, T, P> where } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, P> FusedIterator for SplitMut<'a, T, P> where P: FnMut(&T) -> bool {} /// An iterator over subslices separated by elements that match a predicate @@ -1892,7 +1892,6 @@ impl<'a, T, P> SplitIter for RSplit<'a, T, P> where P: FnMut(&T) -> bool { } } -//#[stable(feature = "fused", since = "1.25.0")] #[unstable(feature = "slice_rsplit", issue = "41020")] impl<'a, T, P> FusedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool {} @@ -1951,7 +1950,6 @@ impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> where } } -//#[stable(feature = "fused", since = "1.25.0")] #[unstable(feature = "slice_rsplit", issue = "41020")] impl<'a, T, P> FusedIterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool {} @@ -2088,7 +2086,7 @@ macro_rules! forward_iterator { } } - #[stable(feature = "fused", since = "1.25.0")] + #[stable(feature = "fused", since = "1.26.0")] impl<'a, $elem, P> FusedIterator for $name<'a, $elem, P> where P: FnMut(&T) -> bool {} } @@ -2194,7 +2192,7 @@ impl<'a, T> DoubleEndedIterator for Windows<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Windows<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Windows<'a, T> {} #[doc(hidden)] @@ -2313,7 +2311,7 @@ impl<'a, T> DoubleEndedIterator for Chunks<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for Chunks<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Chunks<'a, T> {} #[doc(hidden)] @@ -2429,7 +2427,7 @@ impl<'a, T> DoubleEndedIterator for ChunksMut<'a, T> { #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> ExactSizeIterator for ChunksMut<'a, T> {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for ChunksMut<'a, T> {} #[doc(hidden)] @@ -2539,7 +2537,7 @@ impl<'a, T> ExactSizeIterator for ExactChunks<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[unstable(feature = "exact_chunks", issue = "47115")] impl<'a, T> FusedIterator for ExactChunks<'a, T> {} #[doc(hidden)] @@ -2636,7 +2634,7 @@ impl<'a, T> ExactSizeIterator for ExactChunksMut<'a, T> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[unstable(feature = "exact_chunks", issue = "47115")] impl<'a, T> FusedIterator for ExactChunksMut<'a, T> {} #[doc(hidden)] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7e919b653f2..e225c9522bc 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -609,7 +609,7 @@ impl<'a> DoubleEndedIterator for Chars<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Chars<'a> {} impl<'a> Chars<'a> { @@ -702,7 +702,7 @@ impl<'a> DoubleEndedIterator for CharIndices<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for CharIndices<'a> {} impl<'a> CharIndices<'a> { @@ -817,7 +817,7 @@ impl<'a> ExactSizeIterator for Bytes<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Bytes<'a> {} #[unstable(feature = "trusted_len", issue = "37572")] @@ -977,10 +977,10 @@ macro_rules! generate_pattern_iterators { } } - #[stable(feature = "fused", since = "1.25.0")] + #[stable(feature = "fused", since = "1.26.0")] impl<'a, P: Pattern<'a>> FusedIterator for $forward_iterator<'a, P> {} - #[stable(feature = "fused", since = "1.25.0")] + #[stable(feature = "fused", since = "1.26.0")] impl<'a, P: Pattern<'a>> FusedIterator for $reverse_iterator<'a, P> where P::Searcher: ReverseSearcher<'a> {} @@ -1337,7 +1337,7 @@ impl<'a> DoubleEndedIterator for Lines<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Lines<'a> {} /// Created with the method [`lines_any`]. @@ -1403,7 +1403,7 @@ impl<'a> DoubleEndedIterator for LinesAny<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] #[allow(deprecated)] impl<'a> FusedIterator for LinesAny<'a> {} diff --git a/src/libstd/ascii.rs b/src/libstd/ascii.rs index ce3e38de7eb..d5bf9e9bb2f 100644 --- a/src/libstd/ascii.rs +++ b/src/libstd/ascii.rs @@ -590,7 +590,7 @@ impl DoubleEndedIterator for EscapeDefault { } #[stable(feature = "rust1", since = "1.0.0")] impl ExactSizeIterator for EscapeDefault {} -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for EscapeDefault {} #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 9e48efeb113..e86264569a0 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1750,7 +1750,7 @@ impl<'a, K, V> ExactSizeIterator for Iter<'a, K, V> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1773,7 +1773,7 @@ impl<'a, K, V> ExactSizeIterator for IterMut<'a, K, V> { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1808,7 +1808,7 @@ impl ExactSizeIterator for IntoIter { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1840,7 +1840,7 @@ impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1863,7 +1863,7 @@ impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Values<'a, K, V> {} #[stable(feature = "map_values_mut", since = "1.10.0")] @@ -1886,7 +1886,7 @@ impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1921,7 +1921,7 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { self.inner.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K, V> FusedIterator for Drain<'a, K, V> {} #[stable(feature = "std_debug", since = "1.16.0")] diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 7a46603b2db..f41b7a62918 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -1097,7 +1097,7 @@ impl<'a, K> ExactSizeIterator for Iter<'a, K> { self.iter.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K> FusedIterator for Iter<'a, K> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1124,7 +1124,7 @@ impl ExactSizeIterator for IntoIter { self.iter.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for IntoIter {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1155,7 +1155,7 @@ impl<'a, K> ExactSizeIterator for Drain<'a, K> { self.iter.len() } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, K> FusedIterator for Drain<'a, K> {} #[stable(feature = "std_debug", since = "1.16.0")] @@ -1208,7 +1208,7 @@ impl<'a, T, S> fmt::Debug for Intersection<'a, T, S> } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, S> FusedIterator for Intersection<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1244,7 +1244,7 @@ impl<'a, T, S> Iterator for Difference<'a, T, S> } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, S> FusedIterator for Difference<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1283,7 +1283,7 @@ impl<'a, T, S> Iterator for SymmetricDifference<'a, T, S> } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, S> FusedIterator for SymmetricDifference<'a, T, S> where T: Eq + Hash, S: BuildHasher @@ -1307,7 +1307,7 @@ impl<'a, T, S> Clone for Union<'a, T, S> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a, T, S> FusedIterator for Union<'a, T, S> where T: Eq + Hash, S: BuildHasher diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 0c54fb00d2d..cd2af99d6ac 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -905,7 +905,7 @@ impl<'a> DoubleEndedIterator for Iter<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Iter<'a> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1008,7 +1008,7 @@ impl<'a> DoubleEndedIterator for Components<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for Components<'a> {} #[stable(feature = "rust1", since = "1.0.0")] @@ -1076,7 +1076,7 @@ impl<'a> Iterator for Ancestors<'a> { } } -#[unstable(feature = "fused", issue = "35602")] +#[unstable(feature = "path_ancestors", issue = "48581")] impl<'a> FusedIterator for Ancestors<'a> {} //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libstd_unicode/char.rs b/src/libstd_unicode/char.rs index 2bbbf6c0655..8d3df749ed7 100644 --- a/src/libstd_unicode/char.rs +++ b/src/libstd_unicode/char.rs @@ -70,7 +70,7 @@ impl Iterator for ToLowercase { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ToLowercase {} /// Returns an iterator that yields the uppercase equivalent of a `char`. @@ -92,7 +92,7 @@ impl Iterator for ToUppercase { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl FusedIterator for ToUppercase {} #[derive(Debug)] diff --git a/src/libstd_unicode/u_str.rs b/src/libstd_unicode/u_str.rs index ed2f205b580..a72e1210d93 100644 --- a/src/libstd_unicode/u_str.rs +++ b/src/libstd_unicode/u_str.rs @@ -127,7 +127,6 @@ impl Iterator for Utf16Encoder } } -#[stable(feature = "fused", since = "1.25.0")] impl FusedIterator for Utf16Encoder where I: FusedIterator {} @@ -186,5 +185,5 @@ impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { } } -#[stable(feature = "fused", since = "1.25.0")] +#[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for SplitWhitespace<'a> {} -- cgit 1.4.1-3-g733a5 From 683bdc7f0a20236c7dd5a8a731951ef5db14b3be Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Sun, 4 Mar 2018 09:00:09 +0900 Subject: Add comments --- src/liballoc/str.rs | 60 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 40 insertions(+), 20 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 08ba4a180ed..6d153bf02b3 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -2071,35 +2071,55 @@ impl str { return String::new(); } - // n = 2^j + k (2^j > k) - - // 2^j: - let mut s = Vec::with_capacity(self.len() * n); - s.extend(self.as_bytes()); - let mut m = n >> 1; - while m > 0 { - let len = s.len(); - unsafe { - ptr::copy_nonoverlapping(s.as_ptr(), (s.as_mut_ptr() as *mut u8).add(len), len); - s.set_len(len * 2); + // If `n` is larger than zero, it can be split as + // `n = 2^expn + rem (2^expn > rem, expn >= 0, rem >= 0)`. + // `2^expn` is the number represented by the leftmost '1' bit of `n`, + // and `rem` is the remaining part of `n`. + + // Using `Vec` to access `set_len()`. + let mut buf = Vec::with_capacity(self.len() * n); + + // `2^expn` repetition is done by doubling `buf` `expn`-times. + buf.extend(self.as_bytes()); + { + let mut m = n >> 1; + // If `m > 0`, there are remaining bits up to the leftmost '1'. + while m > 0 { + // `buf.extend(buf)`: + unsafe { + ptr::copy_nonoverlapping( + buf.as_ptr(), + (buf.as_mut_ptr() as *mut u8).add(buf.len()), + buf.len(), + ); + // `buf` has capacity of `self.len() * n`. + let buf_len = buf.len(); + buf.set_len(buf_len * 2); + } + + m >>= 1; } - m >>= 1; } - // k: - let res_len = n * self.len(); - if res_len > s.len() { + // `rem` (`= n - 2^expn`) repetition is done by copying + // first `rem` repetitions from `buf` itself. + let rem_len = self.len() * n - buf.len(); // `self.len() * rem` + if rem_len > 0 { + // `buf.extend(buf[0 .. rem_len])`: unsafe { + // This is non-overlapping since `2^expn > rem`. ptr::copy_nonoverlapping( - s.as_ptr(), - (s.as_mut_ptr() as *mut u8).add(s.len()), - res_len - s.len(), + buf.as_ptr(), + (buf.as_mut_ptr() as *mut u8).add(buf.len()), + rem_len, ); - s.set_len(res_len); + // `buf.len() + rem_len` equals to `buf.capacity()` (`self.len() * n`). + let buf_len = buf.len(); + buf.set_len(buf_len + rem_len); } } - unsafe { String::from_utf8_unchecked(s) } + unsafe { String::from_utf8_unchecked(buf) } } /// Checks if all characters in this string are within the ASCII range. -- cgit 1.4.1-3-g733a5 From 3d58543d49266a7ec3eb5f5f2ffaf902fce17c53 Mon Sep 17 00:00:00 2001 From: Shotaro Yamada Date: Sun, 4 Mar 2018 09:43:29 +0900 Subject: Avoid unnecessary calculation --- src/liballoc/str.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 6d153bf02b3..64e815b1fba 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -2113,9 +2113,9 @@ impl str { (buf.as_mut_ptr() as *mut u8).add(buf.len()), rem_len, ); - // `buf.len() + rem_len` equals to `buf.capacity()` (`self.len() * n`). - let buf_len = buf.len(); - buf.set_len(buf_len + rem_len); + // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). + let buf_cap = buf.capacity(); + buf.set_len(buf_cap); } } -- cgit 1.4.1-3-g733a5 From a63bf3bb10117af94cc8189e9f228a81da5b432d Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Fri, 9 Mar 2018 14:08:59 +0100 Subject: Add missing urls --- src/liballoc/vec.rs | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2f57c53a6d8..1bb2bed463b 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1212,8 +1212,9 @@ impl Vec { /// difference, with each additional slot filled with `value`. /// If `new_len` is less than `len`, the `Vec` is simply truncated. /// - /// This method requires `Clone` to clone the passed value. If you'd - /// rather create a value with `Default` instead, see [`resize_default`]. + /// This method requires [`Clone`] to be able clone the passed value. If + /// you'd rather create a value with [`Default`] instead, see + /// [`resize_default`]. /// /// # Examples /// @@ -1227,6 +1228,8 @@ impl Vec { /// assert_eq!(vec, [1, 2]); /// ``` /// + /// [`Clone`]: ../../std/clone/trait.Clone.html + /// [`Default`]: ../../std/default/trait.Default.html /// [`resize_default`]: #method.resize_default #[stable(feature = "vec_resize", since = "1.5.0")] pub fn resize(&mut self, new_len: usize, value: T) { @@ -1244,7 +1247,7 @@ impl Vec { /// Iterates over the slice `other`, clones each element, and then appends /// it to this `Vec`. The `other` vector is traversed in-order. /// - /// Note that this function is same as `extend` except that it is + /// Note that this function is same as [`extend`] except that it is /// specialized to work with slices instead. If and when Rust gets /// specialization this function will likely be deprecated (but still /// available). @@ -1256,6 +1259,8 @@ impl Vec { /// vec.extend_from_slice(&[2, 3, 4]); /// assert_eq!(vec, [1, 2, 3, 4]); /// ``` + /// + /// [`extend`]: #method.extend #[stable(feature = "vec_extend_from_slice", since = "1.6.0")] pub fn extend_from_slice(&mut self, other: &[T]) { self.spec_extend(other.iter()) @@ -1266,12 +1271,11 @@ impl Vec { /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. /// /// If `new_len` is greater than `len`, the `Vec` is extended by the - /// difference, with each additional slot filled with `Default::default()`. + /// difference, with each additional slot filled with [`Default::default()`]. /// If `new_len` is less than `len`, the `Vec` is simply truncated. /// - /// This method uses `Default` to create new values on every push. If - /// you'd rather `Clone` a given value, use [`resize`]. - /// + /// This method uses [`Default`] to create new values on every push. If + /// you'd rather [`Clone`] a given value, use [`resize`]. /// /// # Examples /// @@ -1288,6 +1292,9 @@ impl Vec { /// ``` /// /// [`resize`]: #method.resize + /// [`Default::default()`]: ../../std/default/trait.Default.html#tymethod.default + /// [`Default`]: ../../std/default/trait.Default.html + /// [`Clone`]: ../../std/clone/trait.Clone.html #[unstable(feature = "vec_resize_default", issue = "41758")] pub fn resize_default(&mut self, new_len: usize) { let len = self.len(); -- cgit 1.4.1-3-g733a5 From 994bfd414138e623f315f4273841b9f006ac72ee Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Mon, 26 Feb 2018 09:07:16 -0800 Subject: Update Cargo submodule Required moving all fulldeps tests depending on `rand` to different locations as now there's multiple `rand` crates that can't be implicitly linked against. --- src/Cargo.lock | 702 ++++++++++----------- src/bootstrap/builder.rs | 4 +- src/liballoc/Cargo.toml | 2 +- src/liballoc/tests/binary_heap.rs | 83 ++- src/liballoc/tests/slice.rs | 165 +++++ src/libcore/Cargo.toml | 3 + src/libcore/tests/lib.rs | 1 + src/libcore/tests/num/flt2dec/mod.rs | 1 + src/libcore/tests/num/flt2dec/random.rs | 160 +++++ src/libcore/tests/slice.rs | 71 ++- src/libstd/Cargo.toml | 2 +- src/libstd/tests/env.rs | 95 +++ .../run-pass-fulldeps/binary-heap-panic-safe.rs | 101 --- src/test/run-pass-fulldeps/env.rs | 99 --- src/test/run-pass-fulldeps/flt2dec.rs | 163 ----- src/test/run-pass-fulldeps/sort-unstable.rs | 83 --- .../run-pass-fulldeps/vector-sort-panic-safe.rs | 181 ------ src/tools/cargo | 2 +- src/tools/tidy/src/deps.rs | 2 + 19 files changed, 926 insertions(+), 994 deletions(-) create mode 100644 src/libcore/tests/num/flt2dec/random.rs create mode 100644 src/libstd/tests/env.rs delete mode 100644 src/test/run-pass-fulldeps/binary-heap-panic-safe.rs delete mode 100644 src/test/run-pass-fulldeps/env.rs delete mode 100644 src/test/run-pass-fulldeps/flt2dec.rs delete mode 100644 src/test/run-pass-fulldeps/sort-unstable.rs delete mode 100644 src/test/run-pass-fulldeps/vector-sort-panic-safe.rs (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index 646ddf1a744..3252fda970b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -28,7 +28,7 @@ name = "alloc" version = "0.0.0" dependencies = [ "core 0.0.0", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "std_unicode 0.0.0", ] @@ -39,7 +39,7 @@ dependencies = [ "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", "libc 0.0.0", ] @@ -81,7 +81,7 @@ name = "atty" version = "0.2.8" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -93,8 +93,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "backtrace-sys 0.1.16 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -103,8 +103,8 @@ name = "backtrace-sys" version = "0.1.16" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -131,16 +131,16 @@ name = "bootstrap" version = "0.0.0" dependencies = [ "build_helper 0.1.0", - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -158,8 +158,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "build-manifest" version = "0.1.0" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -177,48 +177,48 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo" -version = "0.26.0" +version = "0.27.0" dependencies = [ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", - "cargotest 0.1.0", - "core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "crates-io 0.15.0", + "core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "crates-io 0.16.0", "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", - "crypto-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "docopt 0.8.3 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "fs2 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "git2-curl 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", + "git2-curl 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "hex 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "home 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", - "ignore 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "ignore 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "jobserver 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", - "scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", + "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -227,9 +227,9 @@ name = "cargo_metadata" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -239,25 +239,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "cargotest" -version = "0.1.0" -dependencies = [ - "cargo 0.26.0", - "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", - "hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -266,7 +250,7 @@ version = "0.1.0" [[package]] name = "cc" -version = "1.0.4" +version = "1.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -279,7 +263,7 @@ name = "chrono" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)", "time 0.1.39 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -308,9 +292,9 @@ dependencies = [ "compiletest_rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "duct 0.8.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -329,11 +313,11 @@ dependencies = [ "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -349,11 +333,11 @@ dependencies = [ "quine-mc_cluskey 0.2.4 (registry+https://github.com/rust-lang/crates.io-index)", "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -361,7 +345,7 @@ name = "cmake" version = "0.1.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -377,14 +361,14 @@ name = "commoncrypto-sys" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "compiler_builtins" version = "0.0.0" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -393,16 +377,16 @@ name = "compiletest" version = "0.0.0" dependencies = [ "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -414,11 +398,11 @@ dependencies = [ "diff 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -429,41 +413,39 @@ version = "0.1.0" [[package]] name = "core" version = "0.0.0" +dependencies = [ + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] [[package]] name = "core-foundation" -version = "0.4.6" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "core-foundation-sys" -version = "0.4.6" +version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "crates-io" -version = "0.15.0" +version = "0.16.0" dependencies = [ "curl 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "crossbeam" -version = "0.2.12" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "crossbeam" version = "0.3.2" @@ -502,14 +484,13 @@ dependencies = [ [[package]] name = "crypto-hash" -version = "0.3.0" +version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "advapi32-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "hex 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -519,11 +500,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", - "schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", - "socket2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", + "schannel 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -532,10 +513,10 @@ name = "curl-sys" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -552,7 +533,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -574,9 +555,9 @@ version = "0.8.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -619,7 +600,7 @@ name = "enum_primitive" version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -637,19 +618,19 @@ version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "env_logger" -version = "0.5.4" +version = "0.5.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -704,7 +685,7 @@ version = "0.1.15" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -721,7 +702,7 @@ name = "flate2" version = "1.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -752,7 +733,7 @@ name = "fs2" version = "0.4.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -782,26 +763,26 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "git2" -version = "0.6.11" +version = "0.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "libgit2-sys 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "libgit2-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)", "openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "git2-curl" -version = "0.7.0" +version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "curl 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)", - "git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)", + "git2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -811,29 +792,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "globset" -version = "0.2.1" +version = "0.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "fnv 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "graphviz" version = "0.0.0" -[[package]] -name = "hamcrest" -version = "0.1.1" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)", -] - [[package]] name = "handlebars" version = "0.29.1" @@ -843,16 +815,11 @@ dependencies = [ "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "pest 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "quick-error 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] -[[package]] -name = "hex" -version = "0.2.0" -source = "registry+https://github.com/rust-lang/crates.io-index" - [[package]] name = "hex" version = "0.3.1" @@ -895,18 +862,19 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "ignore" -version = "0.3.1" +version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)", - "globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", - "lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", + "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", + "globset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -931,7 +899,7 @@ dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)", - "walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", "xz2 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -967,8 +935,8 @@ name = "jobserver" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -983,9 +951,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1003,10 +971,10 @@ version = "0.30.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1025,6 +993,11 @@ name = "lazycell" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "lazycell" +version = "0.6.0" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "libc" version = "0.0.0" @@ -1034,21 +1007,21 @@ dependencies = [ [[package]] name = "libc" -version = "0.2.36" +version = "0.2.39" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "libgit2-sys" -version = "0.6.19" +version = "0.7.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", "curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1058,9 +1031,9 @@ version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1069,8 +1042,8 @@ name = "libz-sys" version = "1.0.18" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1108,9 +1081,9 @@ name = "lzma-sys" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1126,7 +1099,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)", "itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1135,12 +1108,12 @@ dependencies = [ "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)", "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "toml-query 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1150,7 +1123,7 @@ name = "memchr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1158,7 +1131,7 @@ name = "memchr" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1171,8 +1144,8 @@ name = "miniz-sys" version = "0.1.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1181,7 +1154,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)", + "net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1191,7 +1164,7 @@ name = "miow" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "socket2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", + "socket2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1203,7 +1176,7 @@ dependencies = [ "cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", "compiletest_rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1212,14 +1185,12 @@ version = "0.1.0" [[package]] name = "net2" -version = "0.2.31" +version = "0.2.32" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", - "ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1234,7 +1205,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1245,68 +1216,42 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "num" -version = "0.1.41" +version = "0.1.42" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)", - "num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-bigint" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "num-complex" -version = "0.1.41" -source = "registry+https://github.com/rust-lang/crates.io-index" -dependencies = [ - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-integer" -version = "0.1.35" +version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-iter" -version = "0.1.34" +version = "0.1.35" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", + "num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] -name = "num-rational" -version = "0.1.40" +name = "num-traits" +version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "num-traits" -version = "0.1.41" +version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1314,7 +1259,7 @@ name = "num_cpus" version = "1.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1324,14 +1269,14 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl" -version = "0.9.23" +version = "0.10.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "bitflags 0.9.1 (registry+https://github.com/rust-lang/crates.io-index)", + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "foreign-types 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1341,11 +1286,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "openssl-sys" -version = "0.9.24" +version = "0.9.27" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "pkg-config 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1401,8 +1346,8 @@ version = "0.2.9" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1444,7 +1389,7 @@ dependencies = [ name = "profiler_builtins" version = "0.0.0" dependencies = [ - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", "core 0.0.0", ] @@ -1514,11 +1459,22 @@ dependencies = [ [[package]] name = "rand" -version = "0.3.20" +version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rand" +version = "0.4.2" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "fuchsia-zircon 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1537,9 +1493,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1577,12 +1533,12 @@ dependencies = [ [[package]] name = "regex" -version = "0.2.5" +version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "aho-corasick 0.6.4 (registry+https://github.com/rust-lang/crates.io-index)", "memchr 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "regex-syntax 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)", "thread_local 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "utf8-ranges 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1597,6 +1553,14 @@ name = "regex-syntax" version = "0.4.2" source = "registry+https://github.com/rust-lang/crates.io-index" +[[package]] +name = "regex-syntax" +version = "0.5.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "remote-test-client" version = "0.1.0" @@ -1605,14 +1569,23 @@ version = "0.1.0" name = "remote-test-server" version = "0.1.0" +[[package]] +name = "remove_dir_all" +version = "0.3.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +dependencies = [ + "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", +] + [[package]] name = "rls" version = "0.126.0" dependencies = [ - "cargo 0.26.0", + "cargo 0.27.0", "cargo_metadata 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "clippy_lints 0.0.186 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "json 0.11.12 (registry+https://github.com/rust-lang/crates.io-index)", "jsonrpc-core 8.0.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1629,10 +1602,10 @@ dependencies = [ "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rls-vfs 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "rustfmt-nightly 0.3.8", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1660,8 +1633,8 @@ source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rls-span 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1675,8 +1648,8 @@ version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1789,7 +1762,7 @@ dependencies = [ [[package]] name = "rustc-demangle" -version = "0.1.5" +version = "0.1.7" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -1839,7 +1812,7 @@ name = "rustc_back" version = "0.0.0" dependencies = [ "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", "syntax 0.0.0", ] @@ -1893,7 +1866,7 @@ version = "0.0.0" dependencies = [ "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "arena 0.0.0", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "graphviz 0.0.0", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", @@ -1927,7 +1900,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "serialize 0.0.0", "syntax_pos 0.0.0", - "termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)", + "termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -1937,7 +1910,7 @@ version = "0.0.0" dependencies = [ "graphviz 0.0.0", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", "rustc_data_structures 0.0.0", "serialize 0.0.0", @@ -1962,8 +1935,8 @@ version = "0.0.0" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "build_helper 0.1.0", - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_cratesio_shim 0.0.0", ] @@ -2100,15 +2073,15 @@ name = "rustc_trans" version = "0.0.0" dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)", "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "jobserver 0.1.9 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc 0.0.0", - "rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_apfloat 0.0.0", "rustc_back 0.0.0", @@ -2123,7 +2096,7 @@ dependencies = [ "serialize 0.0.0", "syntax 0.0.0", "syntax_pos 0.0.0", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2174,7 +2147,7 @@ name = "rustdoc" version = "0.0.0" dependencies = [ "pulldown-cmark 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", + "tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2199,14 +2172,14 @@ dependencies = [ "getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)", "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-rustc_errors 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-syntax 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", "unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2223,7 +2196,7 @@ dependencies = [ [[package]] name = "schannel" -version = "0.1.10" +version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2232,7 +2205,7 @@ dependencies = [ [[package]] name = "scoped-tls" -version = "0.1.0" +version = "0.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -2259,7 +2232,7 @@ version = "0.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2268,7 +2241,7 @@ version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2278,26 +2251,27 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde" -version = "1.0.27" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "serde_derive" -version = "1.0.27" +version = "1.0.29" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "quote 0.3.15 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)", - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_derive_internals" -version = "0.19.0" +version = "0.20.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)", - "synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)", + "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", + "syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2305,18 +2279,18 @@ name = "serde_ignored" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "serde_json" -version = "1.0.9" +version = "1.0.10" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "dtoa 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "itoa 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", - "num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)", - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2329,7 +2303,7 @@ version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2350,11 +2324,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "socket2" -version = "0.3.0" +version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2377,7 +2351,7 @@ dependencies = [ "panic_abort 0.0.0", "panic_unwind 0.0.0", "profiler_builtins 0.0.0", - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_asan 0.0.0", "rustc_lsan 0.0.0", "rustc_msan 0.0.0", @@ -2410,7 +2384,7 @@ dependencies = [ [[package]] name = "syn" -version = "0.12.13" +version = "0.12.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "proc-macro2 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2474,7 +2448,7 @@ name = "syntex_errors" version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_pos 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2496,7 +2470,7 @@ version = "0.52.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "bitflags 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)", "syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2511,17 +2485,18 @@ version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "filetime 0.1.15 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "xattr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] name = "tempdir" -version = "0.3.5" +version = "0.3.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)", + "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", + "remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2539,10 +2514,10 @@ dependencies = [ [[package]] name = "termcolor" -version = "0.3.3" +version = "0.3.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2550,7 +2525,7 @@ name = "termion" version = "1.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2577,7 +2552,7 @@ version = "2.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2601,9 +2576,9 @@ dependencies = [ name = "tidy" version = "0.1.0" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2611,7 +2586,7 @@ name = "time" version = "0.1.39" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", "redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -2629,7 +2604,7 @@ name = "toml" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2640,10 +2615,15 @@ dependencies = [ "error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)", "is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)", - "regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)", + "regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)", ] +[[package]] +name = "ucd-util" +version = "0.1.1" +source = "registry+https://github.com/rust-lang/crates.io-index" + [[package]] name = "unicode-bidi" version = "0.3.4" @@ -2707,7 +2687,7 @@ dependencies = [ [[package]] name = "url" -version = "1.6.0" +version = "1.7.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", @@ -2720,8 +2700,8 @@ name = "url_serde" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)", - "url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)", + "serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)", + "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2760,10 +2740,11 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "walkdir" -version = "2.0.1" +version = "2.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ "same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2797,11 +2778,10 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "wincolor" -version = "0.1.4" +version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "kernel32-sys 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)", - "winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", + "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2822,7 +2802,7 @@ name = "xattr" version = "0.1.11" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2855,7 +2835,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum byteorder 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "652805b7e73fada9d85e9a6682a4abd490cb52d96aeecc12e33a0de34dfd0d23" "checksum cargo_metadata 0.2.3 (registry+https://github.com/rust-lang/crates.io-index)" = "be1057b8462184f634c3a208ee35b0f935cfd94b694b26deadccd98732088d7b" "checksum cargo_metadata 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "20d6fb2b5574726329c85cdba0df0347fddfec3cf9c8b588f9931708280f5643" -"checksum cc 1.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "deaf9ec656256bb25b404c51ef50097207b9cbb29c933d31f92cae5a8a0ffee0" +"checksum cc 1.0.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fedf677519ac9e865c4ff43ef8f930773b37ed6e6ea61b6b83b400a7b5787f49" "checksum cfg-if 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "d4c819a1287eb618df47cc647173c5c4c66ba19d888a6e50d605672aed3140de" "checksum chrono 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7c20ebe0b2b08b0aeddba49c609fe7957ba2e33449882cb186a180bc60682fa9" "checksum clap 2.29.0 (registry+https://github.com/rust-lang/crates.io-index)" = "110d43e343eb29f4f51c1db31beb879d546db27998577e5715270a54bcf41d3f" @@ -2864,14 +2844,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum commoncrypto 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d056a8586ba25a1e4d61cb090900e495952c7886786fc55f909ab2f819b69007" "checksum commoncrypto-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fed34f46747aa73dfaa578069fd8279d2818ade2b55f38f22a9401c7f4083e2" "checksum compiletest_rs 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "6c5aafb5d4a77c6b5fa384fe93c7a9a3561bd88c4b8b8e45187cf5e779b1badc" -"checksum core-foundation 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "8047f547cd6856d45b1cdd75ef8d2f21f3d0e4bf1dab0a0041b0ae9a5dda9c0e" -"checksum core-foundation-sys 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "152195421a2e6497a8179195672e9d4ee8e45ed8c465b626f1606d27a08ebcd5" -"checksum crossbeam 0.2.12 (registry+https://github.com/rust-lang/crates.io-index)" = "bd66663db5a988098a89599d4857919b3acf7f61402e61365acfd3919857b9be" +"checksum core-foundation 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "286e0b41c3a20da26536c6000a280585d519fd07b3956b43aed8a79e9edce980" +"checksum core-foundation-sys 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "716c271e8613ace48344f723b60b900a93150271e5be206212d052bbc0883efa" "checksum crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "24ce9782d4d5c53674646a6a4c1863a21a8fc0cb649b3c94dfc16e45071dea19" "checksum crossbeam-deque 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f739f8c5363aca78cfb059edf753d8f0d36908c348f3d8d1503f03d8b75d9cf3" "checksum crossbeam-epoch 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "59796cc6cbbdc6bb319161349db0c3250ec73ec7fcb763a51065ec4e2e158552" "checksum crossbeam-utils 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "2760899e32a1d58d5abb31129f8fae5de75220bc2176e77ff7c627ae45c918d9" -"checksum crypto-hash 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "34903878eec1694faf53cae8473a088df333181de421d4d3d48061d6559fe602" +"checksum crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "09de9ee0fc255ace04c7fa0763c9395a945c37c8292bb554f8d48361d1dcf1b4" "checksum curl 0.4.11 (registry+https://github.com/rust-lang/crates.io-index)" = "b70fd6394677d3c0e239ff4be6f2b3176e171ffd1c23ffdc541e78dea2b8bb5e" "checksum curl-sys 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f46e49c7125131f5afaded06944d6888b55cbdf8eba05dae73c954019b907961" "checksum derive-new 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "92f8b8e1d6c8a5f5ea0849a0e4c55941576115c62d3fc425e96918bbbeb3d3c2" @@ -2885,7 +2864,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum enum_primitive 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "be4551092f4d519593039259a9ed8daedf0da12e5109c5280338073eaeb81180" "checksum env_logger 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "15abd780e45b3ea4f76b4e9a26ff4843258dd8a3eed2775a0e7368c2e7936c2f" "checksum env_logger 0.4.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3ddf21e73e016298f5cb37d6ef8e8da8e39f91f9ec8b0df44b7deb16a9f8cd5b" -"checksum env_logger 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)" = "f3cc21490995c841d68e00276eba02071ebb269ec24011d5728bd00eabd39e31" +"checksum env_logger 0.5.5 (registry+https://github.com/rust-lang/crates.io-index)" = "f0628f04f7c26ebccf40d7fc2c1cf92236c05ec88cf2132641cc956812312f0f" "checksum error-chain 0.11.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ff511d5dc435d703f4971bc399647c9bc38e20cb41452e3b9feb4765419ed3f3" "checksum error-chain 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "6930e04918388a9a2e41d518c25cf679ccafe26733fb4127dbf21993f2575d46" "checksum failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "934799b6c1de475a012a02dab0ace1ace43789ee4b99bcfbf1a2e3e8ced5de82" @@ -2900,19 +2879,17 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum fuchsia-zircon-sys 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3dcaa9ae7725d12cdb85b3ad99a434db70b468c09ded17e012d86b5c1010f7a7" "checksum futures 0.1.17 (registry+https://github.com/rust-lang/crates.io-index)" = "118b49cac82e04121117cbd3121ede3147e885627d82c4546b87c702debb90c1" "checksum getopts 0.2.15 (registry+https://github.com/rust-lang/crates.io-index)" = "65922871abd2f101a2eb0eaebadc66668e54a87ad9c3dd82520b5f86ede5eff9" -"checksum git2 0.6.11 (registry+https://github.com/rust-lang/crates.io-index)" = "ee5b4bb7cd2a44e6e5ee3a26ba6a9ca10d4ce2771cdc3839bbc54b47b7d1be84" -"checksum git2-curl 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "68676bc784bf0bef83278898929bf64a251e87c0340723d0b93fa096c9c5bf8e" +"checksum git2 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c4813cd7ad02e53275e6e51aaaf21c30f9ef500b579ad7a54a92f6091a7ac296" +"checksum git2-curl 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "27245f4c3a65ab19fd1ab5b52659bd60ed0ce8d0d129202c4737044d1d21db29" "checksum glob 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "8be18de09a56b60ed0edf84bc9df007e30040691af7acd1c41874faac5895bfb" -"checksum globset 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "464627f948c3190ae3d04b1bc6d7dca2f785bda0ac01278e6db129ad383dbeb6" -"checksum hamcrest 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bf088f042a467089e9baa4972f57f9247e42a0cc549ba264c7a04fbb8ecb89d4" +"checksum globset 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1e96ab92362c06811385ae9a34d2698e8a1160745e0c78fbb434a44c8de3fabc" "checksum handlebars 0.29.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fb04af2006ea09d985fef82b81e0eb25337e51b691c76403332378a53d521edc" -"checksum hex 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "d6a22814455d41612f41161581c2883c0c6a1c41852729b17d5ed88f01e153aa" "checksum hex 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "459d3cf58137bb02ad4adeef5036377ff59f066dbb82517b7192e3a5462a2abc" "checksum home 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9f25ae61099d8f3fee8b483df0bd4ecccf4b2731897aad40d50eca1b641fe6db" "checksum humantime 1.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0484fda3e7007f2a4a0d9c3a703ca38c71c54c55602ce4660c419fd32e188c9e" "checksum idna 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "014b298351066f1512874135335d62a789ffe78a9974f94b43ed5621951eaf7d" "checksum if_chain 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "61bb90bdd39e3af69b0172dfc6130f6cd6332bf040fbb9bdd4401d37adbd48b8" -"checksum ignore 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "bb2f0238094bd1b41800fb6eb9b16fdd5e9832ed6053ed91409f0cd5bf28dcfd" +"checksum ignore 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)" = "245bea0ba52531a3739cb8ba99f8689eda13d7faf8c36b6a73ce4421aab42588" "checksum is-match 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7e5b386aef33a1c677be65237cb9d32c3f3ef56bd035949710c4bb13083eb053" "checksum itertools 0.6.5 (registry+https://github.com/rust-lang/crates.io-index)" = "d3f2be4da1690a039e9ae5fd575f706a63ad5a2120f161b1d653c9da3930dd21" "checksum itertools 0.7.6 (registry+https://github.com/rust-lang/crates.io-index)" = "b07332223953b5051bceb67e8c4700aa65291535568e1f12408c43c4a42c0394" @@ -2925,8 +2902,9 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum lazy_static 0.2.11 (registry+https://github.com/rust-lang/crates.io-index)" = "76f033c7ad61445c5b347c7382dd1237847eb1bce590fe50365dcb33d546be73" "checksum lazy_static 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c8f31047daa365f19be14b47c29df4f7c3b581832407daabe6ae77397619237d" "checksum lazycell 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3b585b7a6811fb03aa10e74b278a0f00f8dd9b45dc681f148bb29fa5cb61859b" -"checksum libc 0.2.36 (registry+https://github.com/rust-lang/crates.io-index)" = "1e5d97d6708edaa407429faa671b942dc0f2727222fb6b6539bf1db936e4b121" -"checksum libgit2-sys 0.6.19 (registry+https://github.com/rust-lang/crates.io-index)" = "6eeae66e7b1c995de45cb4e65c5ab438a96a7b4077e448645d4048dc753ad357" +"checksum lazycell 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a6f08839bc70ef4a3fe1d566d5350f519c5912ea86be0df1740a7d247c7fc0ef" +"checksum libc 0.2.39 (registry+https://github.com/rust-lang/crates.io-index)" = "f54263ad99207254cf58b5f701ecb432c717445ea2ee8af387334bdd1a03fdff" +"checksum libgit2-sys 0.7.1 (registry+https://github.com/rust-lang/crates.io-index)" = "1ecbd6428006c321c29b6c8a895f0d90152f1cf4fd8faab69fc436a3d9594f63" "checksum libssh2-sys 0.2.6 (registry+https://github.com/rust-lang/crates.io-index)" = "0db4ec23611747ef772db1c4d650f8bd762f07b461727ec998f953c614024b75" "checksum libz-sys 1.0.18 (registry+https://github.com/rust-lang/crates.io-index)" = "87f737ad6cc6fd6eefe3d9dc5412f1573865bded441300904d2f42269e140f16" "checksum log 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "e19e8d5c34a3e0e2223db8e060f9e8264aeeb5c5fc64a4ee9965c062211c024b" @@ -2941,22 +2919,20 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum miniz-sys 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "609ce024854aeb19a0ef7567d348aaa5a746b32fb72e336df7fcc16869d7e2b4" "checksum miow 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f2f3b1cf331de6896aabf6e9d55dca90356cc9960cca7eaaf408a355ae919" "checksum miow 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)" = "9224c91f82b3c47cf53dcf78dfaa20d6888fbcc5d272d5f2fcdf8a697f3c987d" -"checksum net2 0.2.31 (registry+https://github.com/rust-lang/crates.io-index)" = "3a80f842784ef6c9a958b68b7516bc7e35883c614004dd94959a4dca1b716c09" +"checksum net2 0.2.32 (registry+https://github.com/rust-lang/crates.io-index)" = "9044faf1413a1057267be51b5afba8eb1090bd2231c693664aa1db716fe1eae0" "checksum nibble_vec 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "62e678237a4c70c5f2b917cefd7d080dfbf800421f06e8a59d4e28ef5130fd9e" "checksum nix 0.8.1 (registry+https://github.com/rust-lang/crates.io-index)" = "47e49f6982987135c5e9620ab317623e723bd06738fd85377e8d55f57c8b6487" "checksum nodrop 0.1.12 (registry+https://github.com/rust-lang/crates.io-index)" = "9a2228dca57108069a5262f2ed8bd2e82496d2e074a06d1ccc7ce1687b6ae0a2" -"checksum num 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cc4083e14b542ea3eb9b5f33ff48bd373a92d78687e74f4cc0a30caeb754f0ca" -"checksum num-bigint 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "bdc1494b5912f088f260b775799468d9b9209ac60885d8186a547a0476289e23" -"checksum num-complex 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "58de7b4bf7cf5dbecb635a5797d489864eadd03b107930cbccf9e0fd7428b47c" -"checksum num-integer 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "d1452e8b06e448a07f0e6ebb0bb1d92b8890eea63288c0b627331d53514d0fba" -"checksum num-iter 0.1.34 (registry+https://github.com/rust-lang/crates.io-index)" = "7485fcc84f85b4ecd0ea527b14189281cf27d60e583ae65ebc9c088b13dffe01" -"checksum num-rational 0.1.40 (registry+https://github.com/rust-lang/crates.io-index)" = "0c7cb72a95250d8a370105c828f388932373e0e94414919891a0f945222310fe" -"checksum num-traits 0.1.41 (registry+https://github.com/rust-lang/crates.io-index)" = "cacfcab5eb48250ee7d0c7896b51a2c5eec99c1feea5f32025635f5ae4b00070" +"checksum num 0.1.42 (registry+https://github.com/rust-lang/crates.io-index)" = "4703ad64153382334aa8db57c637364c322d3372e097840c72000dabdcf6156e" +"checksum num-integer 0.1.36 (registry+https://github.com/rust-lang/crates.io-index)" = "f8d26da319fb45674985c78f1d1caf99aa4941f785d384a2ae36d0740bc3e2fe" +"checksum num-iter 0.1.35 (registry+https://github.com/rust-lang/crates.io-index)" = "4b226df12c5a59b63569dd57fafb926d91b385dfce33d8074a412411b689d593" +"checksum num-traits 0.1.43 (registry+https://github.com/rust-lang/crates.io-index)" = "92e5113e9fd4cc14ded8e499429f396a20f98c772a47cc8622a736e1ec843c31" +"checksum num-traits 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "0b3c2bd9b9d21e48e956b763c9f37134dc62d9e95da6edb3f672cacb6caf3cd3" "checksum num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c51a3322e4bca9d212ad9a158a02abc6934d005490c054a2778df73a70aa0a30" "checksum open 1.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "c281318d992e4432cfa799969467003d05921582a7489a8325e37f8a450d5113" -"checksum openssl 0.9.23 (registry+https://github.com/rust-lang/crates.io-index)" = "169a4b9160baf9b9b1ab975418c673686638995ba921683a7f1e01470dcb8854" +"checksum openssl 0.10.5 (registry+https://github.com/rust-lang/crates.io-index)" = "1636c9f1d78af9cbcc50e523bfff4a30274108aad5e86761afd4d31e4e184fa7" "checksum openssl-probe 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "77af24da69f9d9341038eba93a073b1fdaaa1b788221b00a69bce9e762cb32de" -"checksum openssl-sys 0.9.24 (registry+https://github.com/rust-lang/crates.io-index)" = "14ba54ac7d5a4eabd1d5f2c1fdeb7e7c14debfa669d94b983d01b465e767ba9e" +"checksum openssl-sys 0.9.27 (registry+https://github.com/rust-lang/crates.io-index)" = "d6fdc5c4a02e69ce65046f1763a0181107038e02176233acb0b3351d7cc588f9" "checksum os_pipe 0.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "998bfbb3042e715190fe2a41abfa047d7e8cb81374d2977d7f100eacd8619cb1" "checksum owning_ref 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "cdf84f41639e037b484f93433aa3897863b561ed65c6e59c7073d7c561710f37" "checksum parking_lot 0.5.3 (registry+https://github.com/rust-lang/crates.io-index)" = "3e7f7c9857874e54afeb950eebeae662b1e51a2493666d2ea4c0a5d91dcf0412" @@ -2973,15 +2949,18 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum quote 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "1eca14c727ad12702eb4b6bfb5a232287dcf8385cb8ca83a3eeaf6519c44c408" "checksum racer 2.0.12 (registry+https://github.com/rust-lang/crates.io-index)" = "034f1c4528581c40a60e96875467c03315868084e08ff4ceb46a00f7be3b16b4" "checksum radix_trie 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "211c49b6a9995cac0fd1dd9ca60b42cf3a51e151a12eb954b3a9e75513426ee8" -"checksum rand 0.3.20 (registry+https://github.com/rust-lang/crates.io-index)" = "512870020642bb8c221bf68baa1b2573da814f6ccfe5c9699b1c303047abe9b1" +"checksum rand 0.3.22 (registry+https://github.com/rust-lang/crates.io-index)" = "15a732abf9d20f0ad8eeb6f909bf6868722d9a06e1e50802b6a70351f40b4eb1" +"checksum rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "eba5f8cb59cc50ed56be8880a5c7b496bfd9bd26394e176bc67884094145c2c5" "checksum rayon 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "485541959c8ecc49865526fe6c4de9653dd6e60d829d6edf0be228167b60372d" "checksum rayon-core 1.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9d24ad214285a7729b174ed6d3bcfcb80177807f959d95fafd5bfc5c4f201ac8" "checksum redox_syscall 0.1.37 (registry+https://github.com/rust-lang/crates.io-index)" = "0d92eecebad22b767915e4d529f89f28ee96dbbf5a4810d2b844373f136417fd" "checksum redox_termios 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7e891cfe48e9100a70a3b6eb652fef28920c117d366339687bd5576160db0f76" "checksum regex 0.1.80 (registry+https://github.com/rust-lang/crates.io-index)" = "4fd4ace6a8cf7860714a2c2280d6c1f7e6a413486c13298bbc86fd3da019402f" -"checksum regex 0.2.5 (registry+https://github.com/rust-lang/crates.io-index)" = "744554e01ccbd98fff8c457c3b092cd67af62a555a43bfe97ae8a0451f7799fa" +"checksum regex 0.2.7 (registry+https://github.com/rust-lang/crates.io-index)" = "a62bf8bb734ab90b7f234b681b01af396e5d39b028906c210dc04fa1d5e9e5b3" "checksum regex-syntax 0.3.9 (registry+https://github.com/rust-lang/crates.io-index)" = "f9ec002c35e86791825ed294b50008eea9ddfc8def4420124fbc6b08db834957" "checksum regex-syntax 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)" = "8e931c58b93d86f080c734bfd2bce7dd0079ae2331235818133c8be7f422e20e" +"checksum regex-syntax 0.5.0 (registry+https://github.com/rust-lang/crates.io-index)" = "48d7391e7e90e06eaf3aefbe4652464153ecfec64806f3bf77ffc59638a63e77" +"checksum remove_dir_all 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b5d2f806b0fcdabd98acd380dc8daef485e22bcb7cddc811d1337967f2528cf5" "checksum rls-analysis 0.11.1 (registry+https://github.com/rust-lang/crates.io-index)" = "39461c31c9ec26c2f15821d6aaa349216bf71e24e69550d9f8eeded78dc435e1" "checksum rls-blacklist 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "56fb7b8e4850b988fbcf277fbdb1eff36879070d02fc1ca243b559273866973d" "checksum rls-data 0.15.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bea04462e94b5512a78499837eecb7db182ff082144cd1b4bc32ef5d43de6510" @@ -2994,40 +2973,40 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum rustc-ap-serialize 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "e0745fa445ff41c4b6699936cf35ce3ca49502377dd7b3929c829594772c3a7b" "checksum rustc-ap-syntax 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "82efedabe30f393161e11214a9130edfa01ad476372d1c6f3fec1f8d30488c9d" "checksum rustc-ap-syntax_pos 29.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "db9de2e927e280c75b8efab9c5f591ad31082d5d2c4c562c68fdba2ee77286b0" -"checksum rustc-demangle 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "aee45432acc62f7b9a108cc054142dac51f979e69e71ddce7d6fc7adf29e817e" +"checksum rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)" = "11fb43a206a04116ffd7cfcf9bcb941f8eb6cc7ff667272246b0a1c74259a3cb" "checksum rustc-serialize 0.3.24 (registry+https://github.com/rust-lang/crates.io-index)" = "dcf128d1287d2ea9d80910b5f1120d0b8eede3fbf1abe91c40d39ea7d51e6fda" "checksum same-file 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "cfb6eded0b06a0b512c8ddbcf04089138c9b4362c2f696f3c3d76039d68f3637" -"checksum schannel 0.1.10 (registry+https://github.com/rust-lang/crates.io-index)" = "acece75e0f987c48863a6c792ec8b7d6c4177d4a027f8ccc72f849794f437016" -"checksum scoped-tls 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f417c22df063e9450888a7561788e9bd46d3bb3c1466435b4eccb903807f147d" +"checksum schannel 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "fbaffce35eb61c5b00846e73128b0cd62717e7c0ec46abbec132370d013975b4" +"checksum scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "8674d439c964889e2476f474a3bf198cc9e199e77499960893bac5de7e9218a4" "checksum scopeguard 0.1.2 (registry+https://github.com/rust-lang/crates.io-index)" = "59a076157c1e2dc561d8de585151ee6965d910dd4dcb5dabb7ae3e83981a6c57" "checksum scopeguard 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "94258f53601af11e6a49f722422f6e3425c52b06245a5cf9bc09908b174f5e27" "checksum semver 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "7a3186ec9e65071a2095434b1f5bb24838d4e8e130f584c790f6033c79943537" "checksum semver 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "bee2bc909ab2d8d60dab26e8cad85b25d795b14603a0dcb627b78b9d30b6454b" "checksum semver 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1d7eb9ef2c18661902cc47e535f9bc51b78acd254da71d375c2f6720d9a40403" "checksum semver-parser 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "388a1df253eca08550bef6c72392cfe7c30914bf41df5269b68cbd6ff8f570a3" -"checksum serde 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "db99f3919e20faa51bb2996057f5031d8685019b5a06139b1ce761da671b8526" -"checksum serde_derive 1.0.27 (registry+https://github.com/rust-lang/crates.io-index)" = "f4ba7591cfe93755e89eeecdbcc668885624829b020050e6aec99c2a03bd3fd0" -"checksum serde_derive_internals 0.19.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6e03f1c9530c3fb0a0a5c9b826bdd9246a5921ae995d75f512ac917fc4dd55b5" +"checksum serde 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "4763b773978e495252615e814d2ad04773b2c1f85421c7913869a537f35cb406" +"checksum serde_derive 1.0.29 (registry+https://github.com/rust-lang/crates.io-index)" = "8ab31f00ae5574bb643c196d5e302961c122da1c768604c6d16a35c5d551948a" +"checksum serde_derive_internals 0.20.0 (registry+https://github.com/rust-lang/crates.io-index)" = "1fc848d073be32cd982380c06587ea1d433bc1a4c4a111de07ec2286a3ddade8" "checksum serde_ignored 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "190e9765dcedb56be63b6e0993a006c7e3b071a016a304736e4a315dc01fb142" -"checksum serde_json 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)" = "c9db7266c7d63a4c4b7fe8719656ccdd51acf1bed6124b174f933b009fb10bcb" +"checksum serde_json 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)" = "57781ed845b8e742fc2bf306aba8e3b408fe8c366b900e3769fbc39f49eb8b39" "checksum shared_child 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "099b38928dbe4a0a01fcd8c233183072f14a7d126a34bed05880869be66e14cc" "checksum shell-escape 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "dd5cc96481d54583947bfe88bf30c23d53f883c6cd0145368b69989d97b84ef8" "checksum shlex 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "7fdf1b9db47230893d76faad238fd6097fd6d6a9245cd7a4d90dbd639536bbd2" "checksum smallvec 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "44db0ecb22921ef790d17ae13a3f6d15784183ff5f2a01aa32098c7498d2b4b9" -"checksum socket2 0.3.0 (registry+https://github.com/rust-lang/crates.io-index)" = "cf5d5aa364bf61a0d744a293da20381617b6445b89eb524800fab857c5aed2d8" +"checksum socket2 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "78e4c1cde1adbc6bc4c394da2e7727c916b9b7d0b53d6984c890c65c1f4e6437" "checksum stable_deref_trait 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "15132e0e364248108c5e2c02e3ab539be8d6f5d52a01ca9bbf27ed657316f02b" "checksum strsim 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "b4d15c810519a91cf877e7e36e63fe068815c678181439f2f29e2562147c3694" "checksum syn 0.11.11 (registry+https://github.com/rust-lang/crates.io-index)" = "d3b891b9015c88c576343b9b3e41c2c11a51c219ef067b264bd9c8aa9b441dad" -"checksum syn 0.12.13 (registry+https://github.com/rust-lang/crates.io-index)" = "517f6da31bc53bf080b9a77b29fbd0ff8da2f5a2ebd24c73c2238274a94ac7cb" +"checksum syn 0.12.14 (registry+https://github.com/rust-lang/crates.io-index)" = "8c5bc2d6ff27891209efa5f63e9de78648d7801f085e4653701a692ce938d6fd" "checksum synom 0.11.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a393066ed9010ebaed60b9eafa373d4b1baac186dd7e008555b0f702b51945b6" "checksum synstructure 0.6.1 (registry+https://github.com/rust-lang/crates.io-index)" = "3a761d12e6d8dcb4dcf952a7a89b475e3a9d69e4a69307e01a470977642914bd" "checksum syntex_errors 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "9e52bffe6202cfb67587784cf23e0ec5bf26d331eef4922a16d5c42e12aa1e9b" "checksum syntex_pos 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "955ef4b16af4c468e4680d1497f873ff288f557d338180649e18f915af5e15ac" "checksum syntex_syntax 0.52.0 (registry+https://github.com/rust-lang/crates.io-index)" = "76a302e717e348aa372ff577791c3832395650073b8d8432f8b3cb170b34afde" "checksum tar 0.4.14 (registry+https://github.com/rust-lang/crates.io-index)" = "1605d3388ceb50252952ffebab4b5dc43017ead7e4481b175961c283bb951195" -"checksum tempdir 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "87974a6f5c1dfb344d733055601650059a3363de2a6104819293baff662132d6" +"checksum tempdir 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)" = "f73eebdb68c14bcb24aef74ea96079830e7fa7b31a6106e42ea7ee887c1e134e" "checksum term 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)" = "fa63644f74ce96fbeb9b794f66aff2a52d601cbd5e80f4b97123e3899f4570f1" -"checksum termcolor 0.3.3 (registry+https://github.com/rust-lang/crates.io-index)" = "9065bced9c3e43453aa3d56f1e98590b8455b341d2fa191a1090c0dd0b242c75" +"checksum termcolor 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)" = "56c456352e44f9f91f774ddeeed27c1ec60a2455ed66d692059acfb1d731bda1" "checksum termion 1.5.1 (registry+https://github.com/rust-lang/crates.io-index)" = "689a3bdfaab439fd92bc87df5c4c78417d3cbe537487274e9b0b2dce76e92096" "checksum textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)" = "c0b59b6b4b44d867f1370ef1bd91bfb262bf07bf0ae65c202ea2fbc16153b693" "checksum thread-id 2.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a9539db560102d1cef46b8b78ce737ff0bb64e7e18d35b2a5688f7d097d0ff03" @@ -3037,6 +3016,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum toml 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "736b60249cb25337bc196faa43ee12c705e426f3d55c214d73a4e7be06f92cb4" "checksum toml 0.4.5 (registry+https://github.com/rust-lang/crates.io-index)" = "a7540f4ffc193e0d3c94121edb19b055670d369f77d5804db11ae053a45b6e7e" "checksum toml-query 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "6854664bfc6df0360c695480836ee90e2d0c965f06db291d10be9344792d43e8" +"checksum ucd-util 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "fd2be2d6639d0f8fe6cdda291ad456e23629558d466e2789d2c3e9892bda285d" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" @@ -3045,7 +3025,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" "checksum unreachable 1.0.0 (registry+https://github.com/rust-lang/crates.io-index)" = "382810877fe448991dfc7f0dd6e3ae5d58088fd0ea5e35189655f84e6814fa56" -"checksum url 1.6.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fa35e768d4daf1d85733418a49fb42e10d7f633e394fccab4ab7aba897053fe2" +"checksum url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)" = "f808aadd8cfec6ef90e4a14eb46f24511824d1ac596b9682703c87056c8678b7" "checksum url_serde 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "74e7d099f1ee52f823d4bdd60c93c3602043c728f5db3b97bdb548467f7bddea" "checksum userenv-sys 0.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "71d28ea36bbd9192d75bd9fa9b39f96ddb986eaee824adae5d53b6e51919b2f3" "checksum utf8-ranges 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)" = "a1ca13c08c41c9c3e04224ed9ff80461d97e121589ff27c753a16cb10830ae0f" @@ -3053,13 +3033,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum vcpkg 0.2.2 (registry+https://github.com/rust-lang/crates.io-index)" = "9e0a7d8bed3178a8fb112199d466eeca9ed09a14ba8ad67718179b4fd5487d0b" "checksum vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)" = "887b5b631c2ad01628bbbaa7dd4c869f80d3186688f8d0b6f58774fbe324988c" "checksum void 1.0.2 (registry+https://github.com/rust-lang/crates.io-index)" = "6a02e4885ed3bc0f2de90ea6dd45ebcbb66dacffe03547fadbb0eeae2770887d" -"checksum walkdir 2.0.1 (registry+https://github.com/rust-lang/crates.io-index)" = "40b6d201f4f8998a837196b6de9c73e35af14c992cbb92c4ab641d2c2dce52de" +"checksum walkdir 2.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "63636bd0eb3d00ccb8b9036381b526efac53caf112b7783b730ab3f8e44da369" "checksum winapi 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)" = "167dc9d6949a9b857f3451275e911c3f44255842c1f7a76f33c55103a909087a" "checksum winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "04e3bd221fcbe8a271359c04f21a76db7d0c6028862d1bb5512d85e1e2eb5bb3" "checksum winapi-build 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)" = "2d315eee3b34aca4797b2da6b13ed88266e6d612562a0c46390af8299fc699bc" "checksum winapi-i686-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "ac3b87c63620426dd9b991e5ce0329eff545bccbbb34f3be09ff6fb6ab51b7b6" "checksum winapi-x86_64-pc-windows-gnu 0.4.0 (registry+https://github.com/rust-lang/crates.io-index)" = "712e227841d057c1ee1cd2fb22fa7e5a5461ae8e48fa2ca79ec42cfc1931183f" -"checksum wincolor 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "a39ee4464208f6430992ff20154216ab2357772ac871d994c51628d60e58b8b0" +"checksum wincolor 0.1.6 (registry+https://github.com/rust-lang/crates.io-index)" = "eeb06499a3a4d44302791052df005d5232b927ed1a9658146d842165c4de7767" "checksum ws2_32-sys 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "d59cefebd0c892fa2dd6de581e937301d8552cb44489cdff035c6187cb63fa5e" "checksum xattr 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)" = "5f04de8a1346489a2f9e9bd8526b73d135ec554227b17568456e86aa35b6f3fc" "checksum xz2 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "98df591c3504d014dd791d998123ed00a476c7e26dc6b2e873cb55c6ac9e59fa" diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 22656e5a9da..586216b9a58 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -775,7 +775,9 @@ impl<'a> Builder<'a> { // be resolved because MinGW has the import library. The downside is we // don't get newer functions from Windows, but we don't use any of them // anyway. - cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); + if mode != Mode::Tool { + cargo.env("WINAPI_NO_BUNDLED_LIBRARIES", "1"); + } if self.is_very_verbose() { cargo.arg("-v"); diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 0a265ee1376..3bf919b0c00 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -12,7 +12,7 @@ core = { path = "../libcore" } std_unicode = { path = "../libstd_unicode" } [dev-dependencies] -rand = "0.3" +rand = "0.4" [[test]] name = "collectionstests" diff --git a/src/liballoc/tests/binary_heap.rs b/src/liballoc/tests/binary_heap.rs index 06d585f8ea8..5c979d82e55 100644 --- a/src/liballoc/tests/binary_heap.rs +++ b/src/liballoc/tests/binary_heap.rs @@ -8,9 +8,13 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::panic; +use std::cmp; use std::collections::BinaryHeap; use std::collections::binary_heap::{Drain, PeekMut}; +use std::panic::{self, AssertUnwindSafe}; +use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; + +use rand::{thread_rng, Rng}; #[test] fn test_iterator() { @@ -300,3 +304,80 @@ fn assert_covariance() { d } } + +// old binaryheap failed this test +// +// Integrity means that all elements are present after a comparison panics, +// even if the order may not be correct. +// +// Destructors must be called exactly once per element. +#[test] +fn panic_safe() { + static DROP_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; + + #[derive(Eq, PartialEq, Ord, Clone, Debug)] + struct PanicOrd(T, bool); + + impl Drop for PanicOrd { + fn drop(&mut self) { + // update global drop count + DROP_COUNTER.fetch_add(1, Ordering::SeqCst); + } + } + + impl PartialOrd for PanicOrd { + fn partial_cmp(&self, other: &Self) -> Option { + if self.1 || other.1 { + panic!("Panicking comparison"); + } + self.0.partial_cmp(&other.0) + } + } + let mut rng = thread_rng(); + const DATASZ: usize = 32; + const NTEST: usize = 10; + + // don't use 0 in the data -- we want to catch the zeroed-out case. + let data = (1..DATASZ + 1).collect::>(); + + // since it's a fuzzy test, run several tries. + for _ in 0..NTEST { + for i in 1..DATASZ + 1 { + DROP_COUNTER.store(0, Ordering::SeqCst); + + let mut panic_ords: Vec<_> = data.iter() + .filter(|&&x| x != i) + .map(|&x| PanicOrd(x, false)) + .collect(); + let panic_item = PanicOrd(i, true); + + // heapify the sane items + rng.shuffle(&mut panic_ords); + let mut heap = BinaryHeap::from(panic_ords); + let inner_data; + + { + // push the panicking item to the heap and catch the panic + let thread_result = { + let mut heap_ref = AssertUnwindSafe(&mut heap); + panic::catch_unwind(move || { + heap_ref.push(panic_item); + }) + }; + assert!(thread_result.is_err()); + + // Assert no elements were dropped + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert!(drops == 0, "Must not drop items. drops={}", drops); + inner_data = heap.clone().into_vec(); + drop(heap); + } + let drops = DROP_COUNTER.load(Ordering::SeqCst); + assert_eq!(drops, DATASZ); + + let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::>(); + data_sorted.sort(); + assert_eq!(data_sorted, data); + } + } +} diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 1a9d26fd1a2..d9e9d91cea8 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -8,9 +8,15 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use std::cell::Cell; use std::cmp::Ordering::{Equal, Greater, Less}; +use std::cmp::Ordering; use std::mem; +use std::panic; use std::rc::Rc; +use std::sync::atomic::Ordering::Relaxed; +use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize}; +use std::thread; use rand::{Rng, thread_rng}; @@ -1341,3 +1347,162 @@ fn test_copy_from_slice_dst_shorter() { let mut dst = [0; 3]; dst.copy_from_slice(&src); } + +const MAX_LEN: usize = 80; + +static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ + // FIXME #5244: AtomicUsize is not Copy. + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), + AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), +]; + +static VERSIONS: AtomicUsize = ATOMIC_USIZE_INIT; + +#[derive(Clone, Eq)] +struct DropCounter { + x: u32, + id: usize, + version: Cell, +} + +impl PartialEq for DropCounter { + fn eq(&self, other: &Self) -> bool { + self.partial_cmp(other) == Some(Ordering::Equal) + } +} + +impl PartialOrd for DropCounter { + fn partial_cmp(&self, other: &Self) -> Option { + self.version.set(self.version.get() + 1); + other.version.set(other.version.get() + 1); + VERSIONS.fetch_add(2, Relaxed); + self.x.partial_cmp(&other.x) + } +} + +impl Ord for DropCounter { + fn cmp(&self, other: &Self) -> Ordering { + self.partial_cmp(other).unwrap() + } +} + +impl Drop for DropCounter { + fn drop(&mut self) { + DROP_COUNTS[self.id].fetch_add(1, Relaxed); + VERSIONS.fetch_sub(self.version.get(), Relaxed); + } +} + +macro_rules! test { + ($input:ident, $func:ident) => { + let len = $input.len(); + + // Work out the total number of comparisons required to sort + // this array... + let mut count = 0usize; + $input.to_owned().$func(|a, b| { count += 1; a.cmp(b) }); + + // ... and then panic on each and every single one. + for panic_countdown in 0..count { + // Refresh the counters. + VERSIONS.store(0, Relaxed); + for i in 0..len { + DROP_COUNTS[i].store(0, Relaxed); + } + + let v = $input.to_owned(); + let _ = thread::spawn(move || { + let mut v = v; + let mut panic_countdown = panic_countdown; + v.$func(|a, b| { + if panic_countdown == 0 { + SILENCE_PANIC.with(|s| s.set(true)); + panic!(); + } + panic_countdown -= 1; + a.cmp(b) + }) + }).join(); + + // Check that the number of things dropped is exactly + // what we expect (i.e. the contents of `v`). + for (i, c) in DROP_COUNTS.iter().enumerate().take(len) { + let count = c.load(Relaxed); + assert!(count == 1, + "found drop count == {} for i == {}, len == {}", + count, i, len); + } + + // Check that the most recent versions of values were dropped. + assert_eq!(VERSIONS.load(Relaxed), 0); + } + } +} + +thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] // no threads +fn panic_safe() { + let prev = panic::take_hook(); + panic::set_hook(Box::new(move |info| { + if !SILENCE_PANIC.with(|s| s.get()) { + prev(info); + } + })); + + let mut rng = thread_rng(); + + for len in (1..20).chain(70..MAX_LEN) { + for &modulus in &[5, 20, 50] { + for &has_runs in &[false, true] { + let mut input = (0..len) + .map(|id| { + DropCounter { + x: rng.next_u32() % modulus, + id: id, + version: Cell::new(0), + } + }) + .collect::>(); + + if has_runs { + for c in &mut input { + c.x = c.id as u32; + } + + for _ in 0..5 { + let a = rng.gen::() % len; + let b = rng.gen::() % len; + if a < b { + input[a..b].reverse(); + } else { + input.swap(a, b); + } + } + } + + test!(input, sort_by); + test!(input, sort_unstable_by); + } + } + } +} diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 5af63aa970f..24529f7a9d8 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -16,3 +16,6 @@ path = "../libcore/tests/lib.rs" [[bench]] name = "corebenches" path = "../libcore/benches/lib.rs" + +[dev-dependencies] +rand = "0.4" diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index a9c5683e0ef..d08d6b3215d 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -50,6 +50,7 @@ extern crate core; extern crate test; +extern crate rand; mod any; mod array; diff --git a/src/libcore/tests/num/flt2dec/mod.rs b/src/libcore/tests/num/flt2dec/mod.rs index ef0178815f9..04567e25e25 100644 --- a/src/libcore/tests/num/flt2dec/mod.rs +++ b/src/libcore/tests/num/flt2dec/mod.rs @@ -23,6 +23,7 @@ mod strategy { mod dragon; mod grisu; } +mod random; pub fn decode_finite(v: T) -> Decoded { match decode(v).1 { diff --git a/src/libcore/tests/num/flt2dec/random.rs b/src/libcore/tests/num/flt2dec/random.rs new file mode 100644 index 00000000000..315ac4d7d99 --- /dev/null +++ b/src/libcore/tests/num/flt2dec/random.rs @@ -0,0 +1,160 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![cfg(not(target_arch = "wasm32"))] + +use std::i16; +use std::mem; +use std::str; + +use core::num::flt2dec::MAX_SIG_DIGITS; +use core::num::flt2dec::strategy::grisu::format_exact_opt; +use core::num::flt2dec::strategy::grisu::format_shortest_opt; +use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded}; + +use rand::{self, Rand, XorShiftRng}; +use rand::distributions::{IndependentSample, Range}; + +pub fn decode_finite(v: T) -> Decoded { + match decode(v).1 { + FullDecoded::Finite(decoded) => decoded, + full_decoded => panic!("expected finite, got {:?} instead", full_decoded) + } +} + + +fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), + V: FnMut(usize) -> Decoded { + assert!(k <= 1024); + + let mut npassed = 0; // f(x) = Some(g(x)) + let mut nignored = 0; // f(x) = None + + for i in 0..n { + if (i & 0xfffff) == 0 { + println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})", + i, n, nignored, npassed, i - nignored - npassed); + } + + let decoded = v(i); + let mut buf1 = [0; 1024]; + if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { + let mut buf2 = [0; 1024]; + let (len2, e2) = g(&decoded, &mut buf2[..k]); + if e1 == e2 && &buf1[..len1] == &buf2[..len2] { + npassed += 1; + } else { + println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}", + i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1, + str::from_utf8(&buf2[..len2]).unwrap(), e2); + } + } else { + nignored += 1; + } + } + println!("{}({}): done, ignored={} passed={} failed={}", + func, k, nignored, npassed, n - nignored - npassed); + assert!(nignored + npassed == n, + "{}({}): {} out of {} values returns an incorrect value!", + func, k, n - nignored - npassed, n); + (npassed, nignored) +} + +pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000); + iterate("f32_random_equivalence_test", k, n, f, g, |_| { + let i: u32 = f32_range.ind_sample(&mut rng); + let x: f32 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); + let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); + iterate("f64_random_equivalence_test", k, n, f, g, |_| { + let i: u64 = f64_range.ind_sample(&mut rng); + let x: f64 = unsafe {mem::transmute(i)}; + decode_finite(x) + }); +} + +pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) + where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, + G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { + // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, + // so why not simply testing all of them? + // + // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), + // but with `-C opt-level=3 -C lto` this only takes about an hour or so. + + // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges + let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", + k, 0x7f7f_ffff, f, g, |i: usize| { + let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; + decode_finite(x) + }); + assert_eq!((npassed, nignored), (2121451881, 17643158)); +} + +#[test] +fn shortest_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); + f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); +} + +#[test] #[ignore] // it is too expensive +fn shortest_f32_exhaustive_equivalence_test() { + // it is hard to directly test the optimality of the output, but we can at least test if + // two different algorithms agree to each other. + // + // this reports the progress and the number of f32 values returned `None`. + // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: + // `done, ignored=17643158 passed=2121451881 failed=0`. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); +} + +#[test] #[ignore] // it is too expensive +fn shortest_f64_hard_random_equivalence_test() { + // this again probably has to use appropriate rustc flags. + + use core::num::flt2dec::strategy::dragon::format_shortest as fallback; + f64_random_equivalence_test(format_shortest_opt, fallback, + MAX_SIG_DIGITS, 100_000_000); +} + +#[test] +fn exact_f32_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} + +#[test] +fn exact_f64_random_equivalence_test() { + use core::num::flt2dec::strategy::dragon::format_exact as fallback; + for k in 1..21 { + f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), + |d, buf| fallback(d, buf, i16::MIN), k, 1_000); + } +} + diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 13740b95802..53fdfa06827 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -10,7 +10,6 @@ use core::result::Result::{Ok, Err}; - #[test] fn test_position() { let b = [1, 2, 3, 5, 5]; @@ -481,3 +480,73 @@ fn test_rotate_right() { assert_eq!(a[(i + 42) % N], i); } } + +#[test] +#[cfg(not(target_arch = "wasm32"))] +fn sort_unstable() { + use core::cmp::Ordering::{Equal, Greater, Less}; + use core::slice::heapsort; + use rand::{Rng, XorShiftRng}; + + let mut v = [0; 600]; + let mut tmp = [0; 600]; + let mut rng = XorShiftRng::new_unseeded(); + + for len in (2..25).chain(500..510) { + let v = &mut v[0..len]; + let tmp = &mut tmp[0..len]; + + for &modulus in &[5, 10, 100, 1000] { + for _ in 0..100 { + for i in 0..len { + v[i] = rng.gen::() % modulus; + } + + // Sort in default order. + tmp.copy_from_slice(v); + tmp.sort_unstable(); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in ascending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| a.cmp(b)); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Sort in descending order. + tmp.copy_from_slice(v); + tmp.sort_unstable_by(|a, b| b.cmp(a)); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + + // Test heapsort using `<` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a < b); + assert!(tmp.windows(2).all(|w| w[0] <= w[1])); + + // Test heapsort using `>` operator. + tmp.copy_from_slice(v); + heapsort(tmp, |a, b| a > b); + assert!(tmp.windows(2).all(|w| w[0] >= w[1])); + } + } + } + + // Sort using a completely random comparison function. + // This will reorder the elements *somehow*, but won't panic. + for i in 0..v.len() { + v[i] = i as i32; + } + v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap()); + v.sort_unstable(); + for i in 0..v.len() { + assert_eq!(v[i], i as i32); + } + + // Should not panic. + [0i32; 0].sort_unstable(); + [(); 10].sort_unstable(); + [(); 100].sort_unstable(); + + let mut v = [0xDEADBEEFu64]; + v.sort_unstable(); + assert!(v == [0xDEADBEEF]); +} diff --git a/src/libstd/Cargo.toml b/src/libstd/Cargo.toml index c1fe4a89d6a..12017598853 100644 --- a/src/libstd/Cargo.toml +++ b/src/libstd/Cargo.toml @@ -26,7 +26,7 @@ std_unicode = { path = "../libstd_unicode" } unwind = { path = "../libunwind" } [dev-dependencies] -rand = "0.3" +rand = "0.4" [target.x86_64-apple-darwin.dependencies] rustc_asan = { path = "../librustc_asan" } diff --git a/src/libstd/tests/env.rs b/src/libstd/tests/env.rs new file mode 100644 index 00000000000..d4376523691 --- /dev/null +++ b/src/libstd/tests/env.rs @@ -0,0 +1,95 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +extern crate rand; + +use std::env::*; +use std::iter::repeat; +use std::ffi::{OsString, OsStr}; + +use rand::Rng; + +fn make_rand_name() -> OsString { + let mut rng = rand::thread_rng(); + let n = format!("TEST{}", rng.gen_ascii_chars().take(10) + .collect::()); + let n = OsString::from(n); + assert!(var_os(&n).is_none()); + n +} + +fn eq(a: Option, b: Option<&str>) { + assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); +} + +#[test] +fn test_set_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + eq(var_os(&n), Some("VALUE")); +} + +#[test] +fn test_remove_var() { + let n = make_rand_name(); + set_var(&n, "VALUE"); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_set_var_overwrite() { + let n = make_rand_name(); + set_var(&n, "1"); + set_var(&n, "2"); + eq(var_os(&n), Some("2")); + set_var(&n, ""); + eq(var_os(&n), Some("")); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_var_big() { + let mut s = "".to_string(); + let mut i = 0; + while i < 100 { + s.push_str("aaaaaaaaaa"); + i += 1; + } + let n = make_rand_name(); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); +} + +#[test] +#[cfg_attr(target_os = "emscripten", ignore)] +fn test_env_set_get_huge() { + let n = make_rand_name(); + let s = repeat("x").take(10000).collect::(); + set_var(&n, &s); + eq(var_os(&n), Some(&s)); + remove_var(&n); + eq(var_os(&n), None); +} + +#[test] +fn test_env_set_var() { + let n = make_rand_name(); + + let mut e = vars_os(); + set_var(&n, "VALUE"); + assert!(!e.any(|(k, v)| { + &*k == &*n && &*v == "VALUE" + })); + + assert!(vars_os().any(|(k, v)| { + &*k == &*n && &*v == "VALUE" + })); +} diff --git a/src/test/run-pass-fulldeps/binary-heap-panic-safe.rs b/src/test/run-pass-fulldeps/binary-heap-panic-safe.rs deleted file mode 100644 index 6139a7d3201..00000000000 --- a/src/test/run-pass-fulldeps/binary-heap-panic-safe.rs +++ /dev/null @@ -1,101 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(rustc_private, std_panic)] - -extern crate rand; - -use rand::{thread_rng, Rng}; -use std::panic::{self, AssertUnwindSafe}; - -use std::collections::BinaryHeap; -use std::cmp; -use std::sync::atomic::{AtomicUsize, ATOMIC_USIZE_INIT, Ordering}; - -static DROP_COUNTER: AtomicUsize = ATOMIC_USIZE_INIT; - -// old binaryheap failed this test -// -// Integrity means that all elements are present after a comparison panics, -// even if the order may not be correct. -// -// Destructors must be called exactly once per element. -fn test_integrity() { - #[derive(Eq, PartialEq, Ord, Clone, Debug)] - struct PanicOrd(T, bool); - - impl Drop for PanicOrd { - fn drop(&mut self) { - // update global drop count - DROP_COUNTER.fetch_add(1, Ordering::SeqCst); - } - } - - impl PartialOrd for PanicOrd { - fn partial_cmp(&self, other: &Self) -> Option { - if self.1 || other.1 { - panic!("Panicking comparison"); - } - self.0.partial_cmp(&other.0) - } - } - let mut rng = thread_rng(); - const DATASZ: usize = 32; - const NTEST: usize = 10; - - // don't use 0 in the data -- we want to catch the zeroed-out case. - let data = (1..DATASZ + 1).collect::>(); - - // since it's a fuzzy test, run several tries. - for _ in 0..NTEST { - for i in 1..DATASZ + 1 { - DROP_COUNTER.store(0, Ordering::SeqCst); - - let mut panic_ords: Vec<_> = data.iter() - .filter(|&&x| x != i) - .map(|&x| PanicOrd(x, false)) - .collect(); - let panic_item = PanicOrd(i, true); - - // heapify the sane items - rng.shuffle(&mut panic_ords); - let mut heap = BinaryHeap::from(panic_ords); - let inner_data; - - { - // push the panicking item to the heap and catch the panic - let thread_result = { - let mut heap_ref = AssertUnwindSafe(&mut heap); - panic::catch_unwind(move || { - heap_ref.push(panic_item); - }) - }; - assert!(thread_result.is_err()); - - // Assert no elements were dropped - let drops = DROP_COUNTER.load(Ordering::SeqCst); - assert!(drops == 0, "Must not drop items. drops={}", drops); - inner_data = heap.clone().into_vec(); - drop(heap); - } - let drops = DROP_COUNTER.load(Ordering::SeqCst); - assert_eq!(drops, DATASZ); - - let mut data_sorted = inner_data.into_iter().map(|p| p.0).collect::>(); - data_sorted.sort(); - assert_eq!(data_sorted, data); - } - } -} - -fn main() { - test_integrity(); -} - diff --git a/src/test/run-pass-fulldeps/env.rs b/src/test/run-pass-fulldeps/env.rs deleted file mode 100644 index cf2ea732ee1..00000000000 --- a/src/test/run-pass-fulldeps/env.rs +++ /dev/null @@ -1,99 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: --test - -#![feature(rustc_private, std_panic)] - -extern crate rand; - -use std::env::*; -use std::iter::repeat; -use std::ffi::{OsString, OsStr}; - -use rand::Rng; - -fn make_rand_name() -> OsString { - let mut rng = rand::thread_rng(); - let n = format!("TEST{}", rng.gen_ascii_chars().take(10) - .collect::()); - let n = OsString::from(n); - assert!(var_os(&n).is_none()); - n -} - -fn eq(a: Option, b: Option<&str>) { - assert_eq!(a.as_ref().map(|s| &**s), b.map(OsStr::new).map(|s| &*s)); -} - -#[test] -fn test_set_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - eq(var_os(&n), Some("VALUE")); -} - -#[test] -fn test_remove_var() { - let n = make_rand_name(); - set_var(&n, "VALUE"); - remove_var(&n); - eq(var_os(&n), None); -} - -#[test] -fn test_set_var_overwrite() { - let n = make_rand_name(); - set_var(&n, "1"); - set_var(&n, "2"); - eq(var_os(&n), Some("2")); - set_var(&n, ""); - eq(var_os(&n), Some("")); -} - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_var_big() { - let mut s = "".to_string(); - let mut i = 0; - while i < 100 { - s.push_str("aaaaaaaaaa"); - i += 1; - } - let n = make_rand_name(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); -} - -#[test] -#[cfg_attr(target_os = "emscripten", ignore)] -fn test_env_set_get_huge() { - let n = make_rand_name(); - let s = repeat("x").take(10000).collect::(); - set_var(&n, &s); - eq(var_os(&n), Some(&s)); - remove_var(&n); - eq(var_os(&n), None); -} - -#[test] -fn test_env_set_var() { - let n = make_rand_name(); - - let mut e = vars_os(); - set_var(&n, "VALUE"); - assert!(!e.any(|(k, v)| { - &*k == &*n && &*v == "VALUE" - })); - - assert!(vars_os().any(|(k, v)| { - &*k == &*n && &*v == "VALUE" - })); -} diff --git a/src/test/run-pass-fulldeps/flt2dec.rs b/src/test/run-pass-fulldeps/flt2dec.rs deleted file mode 100644 index 3db0644d1ef..00000000000 --- a/src/test/run-pass-fulldeps/flt2dec.rs +++ /dev/null @@ -1,163 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags:--test - -#![feature(rustc_private, flt2dec)] - -extern crate core; -extern crate rand; - -use std::i16; -use std::mem; -use std::str; - -use core::num::flt2dec::MAX_SIG_DIGITS; -use core::num::flt2dec::strategy::grisu::format_exact_opt; -use core::num::flt2dec::strategy::grisu::format_shortest_opt; -use core::num::flt2dec::{decode, DecodableFloat, FullDecoded, Decoded}; - -use rand::{Rand, XorShiftRng}; -use rand::distributions::{IndependentSample, Range}; -pub fn decode_finite(v: T) -> Decoded { - match decode(v).1 { - FullDecoded::Finite(decoded) => decoded, - full_decoded => panic!("expected finite, got {:?} instead", full_decoded) - } -} - - -fn iterate(func: &str, k: usize, n: usize, mut f: F, mut g: G, mut v: V) -> (usize, usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16), - V: FnMut(usize) -> Decoded { - assert!(k <= 1024); - - let mut npassed = 0; // f(x) = Some(g(x)) - let mut nignored = 0; // f(x) = None - - for i in 0..n { - if (i & 0xfffff) == 0 { - println!("in progress, {:x}/{:x} (ignored={} passed={} failed={})", - i, n, nignored, npassed, i - nignored - npassed); - } - - let decoded = v(i); - let mut buf1 = [0; 1024]; - if let Some((len1, e1)) = f(&decoded, &mut buf1[..k]) { - let mut buf2 = [0; 1024]; - let (len2, e2) = g(&decoded, &mut buf2[..k]); - if e1 == e2 && &buf1[..len1] == &buf2[..len2] { - npassed += 1; - } else { - println!("equivalence test failed, {:x}/{:x}: {:?} f(i)={}e{} g(i)={}e{}", - i, n, decoded, str::from_utf8(&buf1[..len1]).unwrap(), e1, - str::from_utf8(&buf2[..len2]).unwrap(), e2); - } - } else { - nignored += 1; - } - } - println!("{}({}): done, ignored={} passed={} failed={}", - func, k, nignored, npassed, n - nignored - npassed); - assert!(nignored + npassed == n, - "{}({}): {} out of {} values returns an incorrect value!", - func, k, n - nignored - npassed, n); - (npassed, nignored) -} - -pub fn f32_random_equivalence_test(f: F, g: G, k: usize, n: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); - let f32_range = Range::new(0x0000_0001u32, 0x7f80_0000); - iterate("f32_random_equivalence_test", k, n, f, g, |_| { - let i: u32 = f32_range.ind_sample(&mut rng); - let x: f32 = unsafe {mem::transmute(i)}; - decode_finite(x) - }); -} - -pub fn f64_random_equivalence_test(f: F, g: G, k: usize, n: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - let mut rng: XorShiftRng = Rand::rand(&mut rand::thread_rng()); - let f64_range = Range::new(0x0000_0000_0000_0001u64, 0x7ff0_0000_0000_0000); - iterate("f64_random_equivalence_test", k, n, f, g, |_| { - let i: u64 = f64_range.ind_sample(&mut rng); - let x: f64 = unsafe {mem::transmute(i)}; - decode_finite(x) - }); -} - -pub fn f32_exhaustive_equivalence_test(f: F, g: G, k: usize) - where F: FnMut(&Decoded, &mut [u8]) -> Option<(usize, i16)>, - G: FnMut(&Decoded, &mut [u8]) -> (usize, i16) { - // we have only 2^23 * (2^8 - 1) - 1 = 2,139,095,039 positive finite f32 values, - // so why not simply testing all of them? - // - // this is of course very stressful (and thus should be behind an `#[ignore]` attribute), - // but with `-C opt-level=3 -C lto` this only takes about an hour or so. - - // iterate from 0x0000_0001 to 0x7f7f_ffff, i.e. all finite ranges - let (npassed, nignored) = iterate("f32_exhaustive_equivalence_test", - k, 0x7f7f_ffff, f, g, |i: usize| { - let x: f32 = unsafe {mem::transmute(i as u32 + 1)}; - decode_finite(x) - }); - assert_eq!((npassed, nignored), (2121451881, 17643158)); -} - -#[test] -fn shortest_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f64_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); - f32_random_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS, 10_000); -} - -#[test] #[ignore] // it is too expensive -fn shortest_f32_exhaustive_equivalence_test() { - // it is hard to directly test the optimality of the output, but we can at least test if - // two different algorithms agree to each other. - // - // this reports the progress and the number of f32 values returned `None`. - // with `--nocapture` (and plenty of time and appropriate rustc flags), this should print: - // `done, ignored=17643158 passed=2121451881 failed=0`. - - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f32_exhaustive_equivalence_test(format_shortest_opt, fallback, MAX_SIG_DIGITS); -} - -#[test] #[ignore] // it is too expensive -fn shortest_f64_hard_random_equivalence_test() { - // this again probably has to use appropriate rustc flags. - - use core::num::flt2dec::strategy::dragon::format_shortest as fallback; - f64_random_equivalence_test(format_shortest_opt, fallback, - MAX_SIG_DIGITS, 100_000_000); -} - -#[test] -fn exact_f32_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; - for k in 1..21 { - f32_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), - |d, buf| fallback(d, buf, i16::MIN), k, 1_000); - } -} - -#[test] -fn exact_f64_random_equivalence_test() { - use core::num::flt2dec::strategy::dragon::format_exact as fallback; - for k in 1..21 { - f64_random_equivalence_test(|d, buf| format_exact_opt(d, buf, i16::MIN), - |d, buf| fallback(d, buf, i16::MIN), k, 1_000); - } -} diff --git a/src/test/run-pass-fulldeps/sort-unstable.rs b/src/test/run-pass-fulldeps/sort-unstable.rs deleted file mode 100644 index af8a691aa3e..00000000000 --- a/src/test/run-pass-fulldeps/sort-unstable.rs +++ /dev/null @@ -1,83 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(rustc_private, sort_internals)] - -extern crate core; -extern crate rand; - -use std::cmp::Ordering::{Equal, Greater, Less}; -use core::slice::heapsort; - -use rand::{Rng, XorShiftRng}; - -fn main() { - let mut v = [0; 600]; - let mut tmp = [0; 600]; - let mut rng = XorShiftRng::new_unseeded(); - - for len in (2..25).chain(500..510) { - let v = &mut v[0..len]; - let tmp = &mut tmp[0..len]; - - for &modulus in &[5, 10, 100, 1000] { - for _ in 0..100 { - for i in 0..len { - v[i] = rng.gen::() % modulus; - } - - // Sort in default order. - tmp.copy_from_slice(v); - tmp.sort_unstable(); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in ascending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| a.cmp(b)); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Sort in descending order. - tmp.copy_from_slice(v); - tmp.sort_unstable_by(|a, b| b.cmp(a)); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - - // Test heapsort using `<` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a < b); - assert!(tmp.windows(2).all(|w| w[0] <= w[1])); - - // Test heapsort using `>` operator. - tmp.copy_from_slice(v); - heapsort(tmp, |a, b| a > b); - assert!(tmp.windows(2).all(|w| w[0] >= w[1])); - } - } - } - - // Sort using a completely random comparison function. - // This will reorder the elements *somehow*, but won't panic. - for i in 0..v.len() { - v[i] = i as i32; - } - v.sort_unstable_by(|_, _| *rng.choose(&[Less, Equal, Greater]).unwrap()); - v.sort_unstable(); - for i in 0..v.len() { - assert_eq!(v[i], i as i32); - } - - // Should not panic. - [0i32; 0].sort_unstable(); - [(); 10].sort_unstable(); - [(); 100].sort_unstable(); - - let mut v = [0xDEADBEEFu64]; - v.sort_unstable(); - assert!(v == [0xDEADBEEF]); -} diff --git a/src/test/run-pass-fulldeps/vector-sort-panic-safe.rs b/src/test/run-pass-fulldeps/vector-sort-panic-safe.rs deleted file mode 100644 index adc72aa0ea2..00000000000 --- a/src/test/run-pass-fulldeps/vector-sort-panic-safe.rs +++ /dev/null @@ -1,181 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-emscripten no threads support - -#![feature(rustc_private)] -#![feature(sort_unstable)] - -extern crate rand; - -use rand::{thread_rng, Rng}; -use std::cell::Cell; -use std::cmp::Ordering; -use std::panic; -use std::sync::atomic::{ATOMIC_USIZE_INIT, AtomicUsize}; -use std::sync::atomic::Ordering::Relaxed; -use std::thread; - -const MAX_LEN: usize = 80; - -static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ - // FIXME #5244: AtomicUsize is not Copy. - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), - AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), -]; - -static VERSIONS: AtomicUsize = ATOMIC_USIZE_INIT; - -#[derive(Clone, Eq)] -struct DropCounter { - x: u32, - id: usize, - version: Cell, -} - -impl PartialEq for DropCounter { - fn eq(&self, other: &Self) -> bool { - self.partial_cmp(other) == Some(Ordering::Equal) - } -} - -impl PartialOrd for DropCounter { - fn partial_cmp(&self, other: &Self) -> Option { - self.version.set(self.version.get() + 1); - other.version.set(other.version.get() + 1); - VERSIONS.fetch_add(2, Relaxed); - self.x.partial_cmp(&other.x) - } -} - -impl Ord for DropCounter { - fn cmp(&self, other: &Self) -> Ordering { - self.partial_cmp(other).unwrap() - } -} - -impl Drop for DropCounter { - fn drop(&mut self) { - DROP_COUNTS[self.id].fetch_add(1, Relaxed); - VERSIONS.fetch_sub(self.version.get(), Relaxed); - } -} - -macro_rules! test { - ($input:ident, $func:ident) => { - let len = $input.len(); - - // Work out the total number of comparisons required to sort - // this array... - let mut count = 0usize; - $input.to_owned().$func(|a, b| { count += 1; a.cmp(b) }); - - // ... and then panic on each and every single one. - for panic_countdown in 0..count { - // Refresh the counters. - VERSIONS.store(0, Relaxed); - for i in 0..len { - DROP_COUNTS[i].store(0, Relaxed); - } - - let v = $input.to_owned(); - let _ = thread::spawn(move || { - let mut v = v; - let mut panic_countdown = panic_countdown; - v.$func(|a, b| { - if panic_countdown == 0 { - SILENCE_PANIC.with(|s| s.set(true)); - panic!(); - } - panic_countdown -= 1; - a.cmp(b) - }) - }).join(); - - // Check that the number of things dropped is exactly - // what we expect (i.e. the contents of `v`). - for (i, c) in DROP_COUNTS.iter().enumerate().take(len) { - let count = c.load(Relaxed); - assert!(count == 1, - "found drop count == {} for i == {}, len == {}", - count, i, len); - } - - // Check that the most recent versions of values were dropped. - assert_eq!(VERSIONS.load(Relaxed), 0); - } - } -} - -thread_local!(static SILENCE_PANIC: Cell = Cell::new(false)); - -fn main() { - let prev = panic::take_hook(); - panic::set_hook(Box::new(move |info| { - if !SILENCE_PANIC.with(|s| s.get()) { - prev(info); - } - })); - - let mut rng = thread_rng(); - - for len in (1..20).chain(70..MAX_LEN) { - for &modulus in &[5, 20, 50] { - for &has_runs in &[false, true] { - let mut input = (0..len) - .map(|id| { - DropCounter { - x: rng.next_u32() % modulus, - id: id, - version: Cell::new(0), - } - }) - .collect::>(); - - if has_runs { - for c in &mut input { - c.x = c.id as u32; - } - - for _ in 0..5 { - let a = rng.gen::() % len; - let b = rng.gen::() % len; - if a < b { - input[a..b].reverse(); - } else { - input.swap(a, b); - } - } - } - - test!(input, sort_by); - test!(input, sort_unstable_by); - } - } - } -} diff --git a/src/tools/cargo b/src/tools/cargo index 1d6dfea44f9..5f83bb4044f 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit 1d6dfea44f97199d5d5c177c7dadcde393eaff9a +Subproject commit 5f83bb4044f32b60d06717c609610f67411fc671 diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index 72414c2a53e..22c6af28cea 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -91,6 +91,7 @@ static WHITELIST: &'static [Crate] = &[ Crate("redox_termios"), Crate("regex"), Crate("regex-syntax"), + Crate("remove_dir_all"), Crate("rustc-demangle"), Crate("smallvec"), Crate("stable_deref_trait"), @@ -99,6 +100,7 @@ static WHITELIST: &'static [Crate] = &[ Crate("terminon"), Crate("termion"), Crate("thread_local"), + Crate("ucd-util"), Crate("unicode-width"), Crate("unreachable"), Crate("utf8-ranges"), -- cgit 1.4.1-3-g733a5 From 4897935e8645e5f1d9d9ef61c78a1cb019c44f89 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 13 Mar 2018 14:08:15 +0100 Subject: Add hexadecimal formatting of integers with fmt::Debug This can be used for integers within a larger types which implements Debug (possibly through derive) but not fmt::UpperHex or fmt::LowerHex. ```rust assert!(format!("{:02x?}", b"Foo\0") == "[46, 6f, 6f, 00]"); assert!(format!("{:02X?}", b"Foo\0") == "[46, 6F, 6F, 00]"); ``` RFC: https://github.com/rust-lang/rfcs/pull/2226 --- src/liballoc/fmt.rs | 2 ++ src/libcore/fmt/mod.rs | 8 +++++++- src/libcore/fmt/num.rs | 8 +++++++- src/libcore/tests/fmt/num.rs | 6 ++++++ src/libfmt_macros/lib.rs | 22 ++++++++++++++++++++-- 5 files changed, 42 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index a092bfb3b0a..2c4cdef03b0 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -113,6 +113,8 @@ //! //! * *nothing* ⇒ [`Display`] //! * `?` ⇒ [`Debug`] +//! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers +//! * `X?` ⇒ [`Debug`] with lower-case hexadecimal integers //! * `o` ⇒ [`Octal`](trait.Octal.html) //! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) //! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 8ad5a9861a0..a31be0e216f 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -333,7 +333,7 @@ impl<'a> ArgumentV1<'a> { // flags available in the v1 format of format_args #[derive(Copy, Clone)] -enum FlagV1 { SignPlus, SignMinus, Alternate, SignAwareZeroPad, } +enum FlagV1 { SignPlus, SignMinus, Alternate, SignAwareZeroPad, DebugLowerHex, DebugUpperHex } impl<'a> Arguments<'a> { /// When using the format_args!() macro, this function is used to generate the @@ -1401,6 +1401,12 @@ impl<'a> Formatter<'a> { self.flags & (1 << FlagV1::SignAwareZeroPad as u32) != 0 } + // FIXME: Decide what public API we want for these two flags. + // https://github.com/rust-lang/rust/issues/48584 + fn debug_lower_hex(&self) -> bool { self.flags & (1 << FlagV1::DebugLowerHex as u32) != 0 } + + fn debug_upper_hex(&self) -> bool { self.flags & (1 << FlagV1::DebugUpperHex as u32) != 0 } + /// Creates a [`DebugStruct`] builder designed to assist with creation of /// [`fmt::Debug`] implementations for structs. /// diff --git a/src/libcore/fmt/num.rs b/src/libcore/fmt/num.rs index 2992e7cf8db..86f1b5a8f28 100644 --- a/src/libcore/fmt/num.rs +++ b/src/libcore/fmt/num.rs @@ -159,7 +159,13 @@ macro_rules! debug { impl fmt::Debug for $T { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(self, f) + if f.debug_lower_hex() { + fmt::LowerHex::fmt(self, f) + } else if f.debug_upper_hex() { + fmt::UpperHex::fmt(self, f) + } else { + fmt::Display::fmt(self, f) + } } } } diff --git a/src/libcore/tests/fmt/num.rs b/src/libcore/tests/fmt/num.rs index 4ddedd91004..bc205ec0582 100644 --- a/src/libcore/tests/fmt/num.rs +++ b/src/libcore/tests/fmt/num.rs @@ -150,3 +150,9 @@ fn test_format_int_twos_complement() { assert!(format!("{}", i32::MIN) == "-2147483648"); assert!(format!("{}", i64::MIN) == "-9223372036854775808"); } + +#[test] +fn test_format_debug_hex() { + assert!(format!("{:02x?}", b"Foo\0") == "[46, 6f, 6f, 00]"); + assert!(format!("{:02X?}", b"Foo\0") == "[46, 6F, 6F, 00]"); +} diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 71519ab21fe..0f45f965104 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -108,6 +108,10 @@ pub enum Flag { /// For numbers, this means that the number will be padded with zeroes, /// and the sign (`+` or `-`) will precede them. FlagSignAwareZeroPad, + /// For Debug / `?`, format integers in lower-case hexadecimal. + FlagDebugLowerHex, + /// For Debug / `?`, format integers in upper-case hexadecimal. + FlagDebugUpperHex, } /// A count is used for the precision and width parameters of an integer, and @@ -377,8 +381,22 @@ impl<'a> Parser<'a> { spec.precision = self.count(); } } - // Finally the actual format specifier - if self.consume('?') { + // Optional radix followed by the actual format specifier + if self.consume('x') { + if self.consume('?') { + spec.flags |= 1 << (FlagDebugLowerHex as u32); + spec.ty = "?"; + } else { + spec.ty = "x"; + } + } else if self.consume('X') { + if self.consume('?') { + spec.flags |= 1 << (FlagDebugUpperHex as u32); + spec.ty = "?"; + } else { + spec.ty = "X"; + } + } else if self.consume('?') { spec.ty = "?"; } else { spec.ty = self.word(); -- cgit 1.4.1-3-g733a5 From 92bfcd2b192e59d12d64acf6f46c1897a3273b3e Mon Sep 17 00:00:00 2001 From: snf Date: Thu, 8 Mar 2018 14:36:43 +0000 Subject: implementing fallible allocation API (try_reserve) for Vec, String and HashMap --- src/liballoc/allocator.rs | 18 +++ src/liballoc/lib.rs | 1 + src/liballoc/raw_vec.rs | 102 ++++++++------ src/liballoc/string.rs | 74 ++++++++++ src/liballoc/tests/lib.rs | 1 + src/liballoc/tests/string.rs | 163 ++++++++++++++++++++++ src/liballoc/tests/vec.rs | 209 +++++++++++++++++++++++++++- src/liballoc/tests/vec_deque.rs | 208 +++++++++++++++++++++++++++ src/liballoc/vec.rs | 78 +++++++++++ src/liballoc/vec_deque.rs | 92 ++++++++++++ src/libstd/collections/hash/map.rs | 96 +++++++++++-- src/libstd/collections/hash/table.rs | 57 +++++--- src/libstd/collections/mod.rs | 3 + src/libstd/lib.rs | 1 + src/test/ui/feature-gate-try_reserve.rs | 14 ++ src/test/ui/feature-gate-try_reserve.stderr | 11 ++ 16 files changed, 1056 insertions(+), 72 deletions(-) create mode 100644 src/test/ui/feature-gate-try_reserve.rs create mode 100644 src/test/ui/feature-gate-try_reserve.stderr (limited to 'src/liballoc') diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs index 55e8c0b430f..fdc4efc66b9 100644 --- a/src/liballoc/allocator.rs +++ b/src/liballoc/allocator.rs @@ -373,6 +373,24 @@ impl fmt::Display for CannotReallocInPlace { } } +/// Augments `AllocErr` with a CapacityOverflow variant. +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +pub enum CollectionAllocErr { + /// Error due to the computed capacity exceeding the collection's maximum + /// (usually `isize::MAX` bytes). + CapacityOverflow, + /// Error due to the allocator (see the `AllocErr` type's docs). + AllocErr(AllocErr), +} + +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From for CollectionAllocErr { + fn from(err: AllocErr) -> Self { + CollectionAllocErr::AllocErr(err) + } +} + /// An implementation of `Alloc` can allocate, reallocate, and /// deallocate arbitrary blocks of data described via `Layout`. /// diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3f306784558..b93e128d508 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -117,6 +117,7 @@ #![feature(staged_api)] #![feature(str_internals)] #![feature(trusted_len)] +#![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(unsize)] diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 621e1906961..229ae54d747 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -15,6 +15,8 @@ use core::ptr::{self, Unique}; use core::slice; use heap::{Alloc, Layout, Heap}; use super::boxed::Box; +use super::allocator::CollectionAllocErr; +use super::allocator::CollectionAllocErr::*; /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases @@ -84,7 +86,7 @@ impl RawVec { let elem_size = mem::size_of::(); let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow"); - alloc_guard(alloc_size); + alloc_guard(alloc_size).expect("capacity overflow"); // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { @@ -308,7 +310,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; let new_layout = Layout::from_size_align_unchecked(new_size, cur.align()); - alloc_guard(new_size); + alloc_guard(new_size).expect("capacity overflow"); let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, cur, new_layout); @@ -367,7 +369,7 @@ impl RawVec { // overflow and the alignment is sufficiently small. let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; - alloc_guard(new_size); + alloc_guard(new_size).expect("capacity overflow"); let ptr = self.ptr() as *mut _; let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); match self.a.grow_in_place(ptr, old_layout, new_layout) { @@ -403,7 +405,9 @@ impl RawVec { /// # Aborts /// /// Aborts on OOM - pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { + pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) + -> Result<(), CollectionAllocErr> { + unsafe { // NOTE: we don't early branch on ZSTs here because we want this // to actually catch "asking for more than usize::MAX" in that case. @@ -413,16 +417,15 @@ impl RawVec { // Don't actually need any more capacity. // Wrapping in case they gave a bad `used_cap`. if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return; + return Ok(()); } // Nothing we can really do about these checks :( - let new_cap = used_cap.checked_add(needed_extra_cap).expect("capacity overflow"); - let new_layout = match Layout::array::(new_cap) { - Some(layout) => layout, - None => panic!("capacity overflow"), - }; - alloc_guard(new_layout.size()); + let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?; + let new_layout = Layout::array::(new_cap).ok_or(CapacityOverflow)?; + + alloc_guard(new_layout.size())?; + let res = match self.current_layout() { Some(layout) => { let old_ptr = self.ptr.as_ptr() as *mut u8; @@ -430,26 +433,34 @@ impl RawVec { } None => self.a.alloc(new_layout), }; - let uniq = match res { - Ok(ptr) => Unique::new_unchecked(ptr as *mut T), - Err(e) => self.a.oom(e), - }; - self.ptr = uniq; + + self.ptr = Unique::new_unchecked(res? as *mut T); self.cap = new_cap; + + Ok(()) } } + pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { + match self.try_reserve_exact(used_cap, needed_extra_cap) { + Err(CapacityOverflow) => panic!("capacity overflow"), + Err(AllocErr(e)) => self.a.oom(e), + Ok(()) => { /* yay */ } + } + } + /// Calculates the buffer's new size given that it'll hold `used_cap + /// needed_extra_cap` elements. This logic is used in amortized reserve methods. /// Returns `(new_capacity, new_alloc_size)`. - fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) -> usize { + fn amortized_new_size(&self, used_cap: usize, needed_extra_cap: usize) + -> Result { + // Nothing we can really do about these checks :( - let required_cap = used_cap.checked_add(needed_extra_cap) - .expect("capacity overflow"); + let required_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?; // Cannot overflow, because `cap <= isize::MAX`, and type of `cap` is `usize`. let double_cap = self.cap * 2; // `double_cap` guarantees exponential growth. - cmp::max(double_cap, required_cap) + Ok(cmp::max(double_cap, required_cap)) } /// Ensures that the buffer contains at least enough space to hold @@ -504,8 +515,9 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` - pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { - unsafe { + pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) + -> Result<(), CollectionAllocErr> { + unsafe { // NOTE: we don't early branch on ZSTs here because we want this // to actually catch "asking for more than usize::MAX" in that case. // If we make it past the first branch then we are guaranteed to @@ -514,17 +526,15 @@ impl RawVec { // Don't actually need any more capacity. // Wrapping in case they give a bad `used_cap` if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return; + return Ok(()); } - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; + let new_layout = Layout::array::(new_cap).ok_or(CapacityOverflow)?; + + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size())?; - let new_layout = match Layout::array::(new_cap) { - Some(layout) => layout, - None => panic!("capacity overflow"), - }; - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size()); let res = match self.current_layout() { Some(layout) => { let old_ptr = self.ptr.as_ptr() as *mut u8; @@ -532,15 +542,22 @@ impl RawVec { } None => self.a.alloc(new_layout), }; - let uniq = match res { - Ok(ptr) => Unique::new_unchecked(ptr as *mut T), - Err(e) => self.a.oom(e), - }; - self.ptr = uniq; + + self.ptr = Unique::new_unchecked(res? as *mut T); self.cap = new_cap; + + Ok(()) } } + /// The same as try_reserve, but errors are lowered to a call to oom(). + pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { + match self.try_reserve(used_cap, needed_extra_cap) { + Err(CapacityOverflow) => panic!("capacity overflow"), + Err(AllocErr(e)) => self.a.oom(e), + Ok(()) => { /* yay */ } + } + } /// Attempts to ensure that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate in place enough space plus comfortable slack @@ -576,7 +593,8 @@ impl RawVec { return false; } - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap); + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap) + .expect("capacity overflow"); // Here, `cap < used_cap + needed_extra_cap <= new_cap` // (regardless of whether `self.cap - used_cap` wrapped). @@ -585,7 +603,7 @@ impl RawVec { let ptr = self.ptr() as *mut _; let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size()); + alloc_guard(new_layout.size()).expect("capacity overflow"); match self.a.grow_in_place(ptr, old_layout, new_layout) { Ok(_) => { self.cap = new_cap; @@ -709,14 +727,14 @@ unsafe impl<#[may_dangle] T, A: Alloc> Drop for RawVec { // all 4GB in user-space. e.g. PAE or x32 #[inline] -fn alloc_guard(alloc_size: usize) { - if mem::size_of::() < 8 { - assert!(alloc_size <= ::core::isize::MAX as usize, - "capacity overflow"); +fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { + if mem::size_of::() < 8 && alloc_size > ::core::isize::MAX as usize { + Err(CapacityOverflow) + } else { + Ok(()) } } - #[cfg(test)] mod tests { use super::*; diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 370fb6b4e89..dcc81417346 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -71,6 +71,7 @@ use Bound::{Excluded, Included, Unbounded}; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; use vec::Vec; use boxed::Box; +use super::allocator::CollectionAllocErr; /// A UTF-8 encoded, growable string. /// @@ -920,6 +921,79 @@ impl String { self.vec.reserve_exact(additional) } + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `String`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// + /// fn process_data(data: &str) -> Result { + /// let mut output = String::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.push_str(data); + /// + /// Ok(output) + /// } + /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.vec.try_reserve(additional) + } + + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `String`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// + /// fn process_data(data: &str) -> Result { + /// let mut output = String::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.push_str(data); + /// + /// Ok(output) + /// } + /// # process_data("rust").expect("why is the test harness OOMing on 4 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.vec.try_reserve_exact(additional) + } + /// Shrinks the capacity of this `String` to match its length. /// /// # Examples diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 168dbb2ce9b..285cba0270c 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -26,6 +26,7 @@ #![feature(splice)] #![feature(str_escape)] #![feature(string_retain)] +#![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(exact_chunks)] diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index ef6f5e10a72..d1e746ea43b 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -9,6 +9,9 @@ // except according to those terms. use std::borrow::Cow; +use std::collections::CollectionAllocErr::*; +use std::mem::size_of; +use std::{usize, isize}; pub trait IntoCow<'a, B: ?Sized> where B: ToOwned { fn into_cow(self) -> Cow<'a, B>; @@ -504,3 +507,163 @@ fn test_into_boxed_str() { let ys = xs.into_boxed_str(); assert_eq!(&*ys, "hello my name is bob"); } + +#[test] +fn test_reserve_exact() { + // This is all the same as test_reserve + + let mut s = String::new(); + assert_eq!(s.capacity(), 0); + + s.reserve_exact(2); + assert!(s.capacity() >= 2); + + for _i in 0..16 { + s.push('0'); + } + + assert!(s.capacity() >= 16); + s.reserve_exact(16); + assert!(s.capacity() >= 32); + + s.push('0'); + + s.reserve_exact(16); + assert!(s.capacity() >= 33) +} + +#[test] +fn test_try_reserve() { + + // These are the interesting cases: + // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) + // * > isize::MAX should always fail + // * On 16/32-bit should CapacityOverflow + // * On 64-bit should OOM + // * overflow may trigger when adding `len` to `cap` (in number of elements) + // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + // On 16/32-bit, we check that allocations don't exceed isize::MAX, + // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. + // Any platform that succeeds for these requests is technically broken with + // ptr::offset because LLVM is the worst. + let guards_against_isize = size_of::() < 8; + + { + // Note: basic stuff is checked by test_reserve + let mut empty_string: String = String::new(); + + // Check isize::MAX doesn't count as an overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + // Play it again, frank! (just to be sure) + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + // Check isize::MAX + 1 does count as overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + // Check usize::MAX does count as overflow + if let Err(CapacityOverflow) = empty_string.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + // Check isize::MAX + 1 is an OOM + if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + + // Check usize::MAX is an OOM + if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an OOM!") } + } + } + + + { + // Same basic idea, but with non-zero len + let mut ten_bytes: String = String::from("0123456789"); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + // Should always overflow in the add-to-len + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + +} + +#[test] +fn test_try_reserve_exact() { + + // This is exactly the same as test_try_reserve with the method changed. + // See that test for comments. + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + let guards_against_isize = size_of::() < 8; + + { + let mut empty_string: String = String::new(); + + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + + if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an OOM!") } + } + } + + + { + let mut ten_bytes: String = String::from("0123456789"); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + +} diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 9cfde5dcc73..3c17a401bba 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -10,8 +10,9 @@ use std::borrow::Cow; use std::mem::size_of; -use std::panic; +use std::{usize, isize, panic}; use std::vec::{Drain, IntoIter}; +use std::collections::CollectionAllocErr::*; struct DropCounter<'a> { count: &'a mut u32, @@ -965,3 +966,209 @@ fn drain_filter_complex() { assert_eq!(vec, vec![1, 3, 5, 7, 9, 11, 13, 15, 17, 19]); } } + +#[test] +fn test_reserve_exact() { + // This is all the same as test_reserve + + let mut v = Vec::new(); + assert_eq!(v.capacity(), 0); + + v.reserve_exact(2); + assert!(v.capacity() >= 2); + + for i in 0..16 { + v.push(i); + } + + assert!(v.capacity() >= 16); + v.reserve_exact(16); + assert!(v.capacity() >= 32); + + v.push(16); + + v.reserve_exact(16); + assert!(v.capacity() >= 33) +} + +#[test] +fn test_try_reserve() { + + // These are the interesting cases: + // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) + // * > isize::MAX should always fail + // * On 16/32-bit should CapacityOverflow + // * On 64-bit should OOM + // * overflow may trigger when adding `len` to `cap` (in number of elements) + // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + // On 16/32-bit, we check that allocations don't exceed isize::MAX, + // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. + // Any platform that succeeds for these requests is technically broken with + // ptr::offset because LLVM is the worst. + let guards_against_isize = size_of::() < 8; + + { + // Note: basic stuff is checked by test_reserve + let mut empty_bytes: Vec = Vec::new(); + + // Check isize::MAX doesn't count as an overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + // Play it again, frank! (just to be sure) + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + // Check isize::MAX + 1 does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + // Check usize::MAX does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + // Check isize::MAX + 1 is an OOM + if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + + // Check usize::MAX is an OOM + if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an OOM!") } + } + } + + + { + // Same basic idea, but with non-zero len + let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + // Should always overflow in the add-to-len + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + + + { + // Same basic idea, but with interesting type size + let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + // Should fail in the mul-by-size + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) { + } else { + panic!("usize::MAX should trigger an overflow!"); + } + } + +} + +#[test] +fn test_try_reserve_exact() { + + // This is exactly the same as test_try_reserve with the method changed. + // See that test for comments. + + const MAX_CAP: usize = isize::MAX as usize; + const MAX_USIZE: usize = usize::MAX; + + let guards_against_isize = size_of::() < 8; + + { + let mut empty_bytes: Vec = Vec::new(); + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + + if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an OOM!") } + } + } + + + { + let mut ten_bytes: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + + + { + let mut ten_u32s: Vec = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + +} diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index f2935c05d4f..fc1a0b624a5 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -11,6 +11,9 @@ use std::collections::VecDeque; use std::fmt::Debug; use std::collections::vec_deque::{Drain}; +use std::collections::CollectionAllocErr::*; +use std::mem::size_of; +use std::{usize, isize}; use self::Taggy::*; use self::Taggypar::*; @@ -1022,3 +1025,208 @@ fn test_placement_in() { } assert_eq!(buf, [5,4,3,1,2,6]); } + +#[test] +fn test_reserve_exact_2() { + // This is all the same as test_reserve + + let mut v = VecDeque::new(); + + v.reserve_exact(2); + assert!(v.capacity() >= 2); + + for i in 0..16 { + v.push_back(i); + } + + assert!(v.capacity() >= 16); + v.reserve_exact(16); + assert!(v.capacity() >= 32); + + v.push_back(16); + + v.reserve_exact(16); + assert!(v.capacity() >= 48) +} + +#[test] +fn test_try_reserve() { + + // These are the interesting cases: + // * exactly isize::MAX should never trigger a CapacityOverflow (can be OOM) + // * > isize::MAX should always fail + // * On 16/32-bit should CapacityOverflow + // * On 64-bit should OOM + // * overflow may trigger when adding `len` to `cap` (in number of elements) + // * overflow may trigger when multiplying `new_cap` by size_of:: (to get bytes) + + const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1; + const MAX_USIZE: usize = usize::MAX; + + // On 16/32-bit, we check that allocations don't exceed isize::MAX, + // on 64-bit, we assume the OS will give an OOM for such a ridiculous size. + // Any platform that succeeds for these requests is technically broken with + // ptr::offset because LLVM is the worst. + let guards_against_isize = size_of::() < 8; + + { + // Note: basic stuff is checked by test_reserve + let mut empty_bytes: VecDeque = VecDeque::new(); + + // Check isize::MAX doesn't count as an overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + // Play it again, frank! (just to be sure) + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + // Check isize::MAX + 1 does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + // Check usize::MAX does count as overflow + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + // Check isize::MAX is an OOM + // VecDeque starts with capacity 7, always adds 1 to the capacity + // and also rounds the number to next power of 2 so this is the + // furthest we can go without triggering CapacityOverflow + if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + } + + + { + // Same basic idea, but with non-zero len + let mut ten_bytes: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + // Should always overflow in the add-to-len + if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + + + { + // Same basic idea, but with interesting type size + let mut ten_u32s: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + // Should fail in the mul-by-size + if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_USIZE - 20) { + } else { + panic!("usize::MAX should trigger an overflow!"); + } + } + +} + +#[test] +fn test_try_reserve_exact() { + + // This is exactly the same as test_try_reserve with the method changed. + // See that test for comments. + + const MAX_CAP: usize = (isize::MAX as usize + 1) / 2 - 1; + const MAX_USIZE: usize = usize::MAX; + + let guards_against_isize = size_of::() < 8; + + { + let mut empty_bytes: VecDeque = VecDeque::new(); + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + + if guards_against_isize { + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + } else { panic!("isize::MAX + 1 should trigger an overflow!") } + + if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } else { + // Check isize::MAX is an OOM + // VecDeque starts with capacity 7, always adds 1 to the capacity + // and also rounds the number to next power of 2 so this is the + // furthest we can go without triggering CapacityOverflow + if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + } + + + { + let mut ten_bytes: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + + + { + let mut ten_u32s: VecDeque = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10].into_iter().collect(); + + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 10) { + panic!("isize::MAX shouldn't trigger an overflow!"); + } + if guards_against_isize { + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an overflow!"); } + } else { + if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { + } else { panic!("usize::MAX should trigger an overflow!") } + } + +} diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 1bb2bed463b..953f95876be 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -86,6 +86,7 @@ use borrow::Cow; use boxed::Box; use raw_vec::RawVec; use super::range::RangeArgument; +use super::allocator::CollectionAllocErr; use Bound::{Excluded, Included, Unbounded}; /// A contiguous growable array type, written `Vec` but pronounced 'vector'. @@ -489,6 +490,83 @@ impl Vec { self.buf.reserve_exact(self.len, additional); } + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `Vec`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = Vec::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.buf.try_reserve(self.len, additional) + } + + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `Vec`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = Vec::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.buf.try_reserve_exact(self.len, additional) + } + /// Shrinks the capacity of the vector as much as possible. /// /// It will drop down as close as possible to the length but the allocator diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 68add3cbd51..0658777f0a0 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -31,6 +31,7 @@ use core::cmp; use raw_vec::RawVec; +use super::allocator::CollectionAllocErr; use super::range::RangeArgument; use Bound::{Excluded, Included, Unbounded}; use super::vec::Vec; @@ -566,6 +567,97 @@ impl VecDeque { } } + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `VecDeque`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// use std::collections::VecDeque; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = VecDeque::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve_exact(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.try_reserve(additional) + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `VecDeque`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// use std::collections::VecDeque; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = VecDeque::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + let old_cap = self.cap(); + let used_cap = self.len() + 1; + let new_cap = used_cap.checked_add(additional) + .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) + .ok_or(CollectionAllocErr::CapacityOverflow)?; + + if new_cap > old_cap { + self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?; + unsafe { + self.handle_cap_increase(old_cap); + } + } + Ok(()) + } + /// Shrinks the capacity of the `VecDeque` as much as possible. /// /// It will drop down as close as possible to the length but the allocator may still inform the diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 6f4528a0e24..b18b38ec302 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,6 +11,8 @@ use self::Entry::*; use self::VacantEntryState::*; +use alloc::heap::{Heap, Alloc}; +use alloc::allocator::CollectionAllocErr; use cell::Cell; use borrow::Borrow; use cmp::max; @@ -42,21 +44,28 @@ impl DefaultResizePolicy { /// provide that capacity, accounting for maximum loading. The raw capacity /// is always zero or a power of two. #[inline] - fn raw_capacity(&self, len: usize) -> usize { + fn try_raw_capacity(&self, len: usize) -> Result { if len == 0 { - 0 + Ok(0) } else { // 1. Account for loading: `raw_capacity >= len * 1.1`. // 2. Ensure it is a power of two. // 3. Ensure it is at least the minimum size. - let mut raw_cap = len * 11 / 10; - assert!(raw_cap >= len, "raw_cap overflow"); - raw_cap = raw_cap.checked_next_power_of_two().expect("raw_capacity overflow"); + let mut raw_cap = len.checked_mul(11) + .map(|l| l / 10) + .and_then(|l| l.checked_next_power_of_two()) + .ok_or(CollectionAllocErr::CapacityOverflow)?; + raw_cap = max(MIN_NONZERO_RAW_CAPACITY, raw_cap); - raw_cap + Ok(raw_cap) } } + #[inline] + fn raw_capacity(&self, len: usize) -> usize { + self.try_raw_capacity(len).expect("raw_capacity overflow") + } + /// The capacity of the given raw capacity. #[inline] fn capacity(&self, raw_cap: usize) -> usize { @@ -775,17 +784,45 @@ impl HashMap /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { + match self.try_reserve(additional) { + Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), + Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Ok(()) => { /* yay */ } + } + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `HashMap`. The collection may reserve more space to avoid + /// frequent reallocations. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::HashMap; + /// let mut map: HashMap<&str, isize> = HashMap::new(); + /// map.try_reserve(10).expect("why is the test harness OOMing on 10 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { let remaining = self.capacity() - self.len(); // this can't overflow if remaining < additional { - let min_cap = self.len().checked_add(additional).expect("reserve overflow"); - let raw_cap = self.resize_policy.raw_capacity(min_cap); - self.resize(raw_cap); + let min_cap = self.len().checked_add(additional) + .ok_or(CollectionAllocErr::CapacityOverflow)?; + let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?; + self.try_resize(raw_cap)?; } else if self.table.tag() && remaining <= self.len() { // Probe sequence is too long and table is half full, // resize early to reduce probing length. let new_capacity = self.table.capacity() * 2; - self.resize(new_capacity); + self.try_resize(new_capacity)?; } + Ok(()) } /// Resizes the internal vectors to a new capacity. It's your @@ -795,15 +832,15 @@ impl HashMap /// 2) Ensure `new_raw_cap` is a power of two or zero. #[inline(never)] #[cold] - fn resize(&mut self, new_raw_cap: usize) { + fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> { assert!(self.table.size() <= new_raw_cap); assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); - let mut old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); + let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?); let old_size = old_table.size(); if old_table.size() == 0 { - return; + return Ok(()); } let mut bucket = Bucket::head_bucket(&mut old_table); @@ -838,6 +875,7 @@ impl HashMap } assert_eq!(self.table.size(), old_size); + Ok(()) } /// Shrinks the capacity of the map as much as possible. It will drop @@ -2717,6 +2755,9 @@ mod test_map { use cell::RefCell; use rand::{thread_rng, Rng}; use panic; + use realstd::collections::CollectionAllocErr::*; + use realstd::mem::size_of; + use realstd::usize; #[test] fn test_zero_capacities() { @@ -3651,4 +3692,33 @@ mod test_map { let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); })); assert_eq!(hm.len(), 0); } + + #[test] + fn test_try_reserve() { + + let mut empty_bytes: HashMap = HashMap::new(); + + const MAX_USIZE: usize = usize::MAX; + + // HashMap and RawTables use complicated size calculations + // hashes_size is sizeof(HashUint) * capacity; + // pairs_size is sizeof((K. V)) * capacity; + // alignment_hashes_size is 8 + // alignment_pairs size is 4 + let size_of_multiplier = (size_of::() + size_of::<(u8, u8)>()).next_power_of_two(); + // The following formula is used to calculate the new capacity + let max_no_ovf = ((MAX_USIZE / 11) * 10) / size_of_multiplier - 1; + + if let Err(CapacityOverflow) = empty_bytes.try_reserve(MAX_USIZE) { + } else { panic!("usize::MAX should trigger an overflow!"); } + + if size_of::() < 8 { + if let Err(CapacityOverflow) = empty_bytes.try_reserve(max_no_ovf) { + } else { panic!("isize::MAX + 1 should trigger a CapacityOverflow!") } + } else { + if let Err(AllocErr(_)) = empty_bytes.try_reserve(max_no_ovf) { + } else { panic!("isize::MAX + 1 should trigger an OOM!") } + } + } + } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 73bd5747c10..8e78dc546c6 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -17,6 +17,7 @@ use mem::{align_of, size_of, needs_drop}; use mem; use ops::{Deref, DerefMut}; use ptr::{self, Unique, NonNull}; +use alloc::allocator::CollectionAllocErr; use self::BucketState::*; @@ -741,14 +742,15 @@ fn test_offset_calculation() { impl RawTable { /// Does not initialize the buckets. The caller should ensure they, /// at the very least, set every hash to EMPTY_BUCKET. - unsafe fn new_uninitialized(capacity: usize) -> RawTable { + /// Returns an error if it cannot allocate or capacity overflows. + unsafe fn try_new_uninitialized(capacity: usize) -> Result, CollectionAllocErr> { if capacity == 0 { - return RawTable { + return Ok(RawTable { size: 0, capacity_mask: capacity.wrapping_sub(1), hashes: TaggedHashUintPtr::new(EMPTY as *mut HashUint), marker: marker::PhantomData, - }; + }); } // No need for `checked_mul` before a more restrictive check performed @@ -768,25 +770,38 @@ impl RawTable { align_of::(), pairs_size, align_of::<(K, V)>()); - assert!(!oflo, "capacity overflow"); + if oflo { + return Err(CollectionAllocErr::CapacityOverflow); + } // One check for overflow that covers calculation and rounding of size. - let size_of_bucket = size_of::().checked_add(size_of::<(K, V)>()).unwrap(); - assert!(size >= - capacity.checked_mul(size_of_bucket) - .expect("capacity overflow"), - "capacity overflow"); + let size_of_bucket = size_of::().checked_add(size_of::<(K, V)>()) + .ok_or(CollectionAllocErr::CapacityOverflow)?; + let capacity_mul_size_of_bucket = capacity.checked_mul(size_of_bucket); + if capacity_mul_size_of_bucket.is_none() || size < capacity_mul_size_of_bucket.unwrap() { + return Err(CollectionAllocErr::CapacityOverflow); + } - let buffer = Heap.alloc(Layout::from_size_align(size, alignment).unwrap()) - .unwrap_or_else(|e| Heap.oom(e)); + let buffer = Heap.alloc(Layout::from_size_align(size, alignment) + .ok_or(CollectionAllocErr::CapacityOverflow)?)?; let hashes = buffer as *mut HashUint; - RawTable { + Ok(RawTable { capacity_mask: capacity.wrapping_sub(1), size: 0, hashes: TaggedHashUintPtr::new(hashes), marker: marker::PhantomData, + }) + } + + /// Does not initialize the buckets. The caller should ensure they, + /// at the very least, set every hash to EMPTY_BUCKET. + unsafe fn new_uninitialized(capacity: usize) -> RawTable { + match Self::try_new_uninitialized(capacity) { + Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), + Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Ok(table) => { table } } } @@ -809,13 +824,23 @@ impl RawTable { } } + /// Tries to create a new raw table from a given capacity. If it cannot allocate, + /// it returns with AllocErr. + pub fn try_new(capacity: usize) -> Result, CollectionAllocErr> { + unsafe { + let ret = RawTable::try_new_uninitialized(capacity)?; + ptr::write_bytes(ret.hashes.ptr(), 0, capacity); + Ok(ret) + } + } + /// Creates a new raw table from a given capacity. All buckets are /// initially empty. pub fn new(capacity: usize) -> RawTable { - unsafe { - let ret = RawTable::new_uninitialized(capacity); - ptr::write_bytes(ret.hashes.ptr(), 0, capacity); - ret + match Self::try_new(capacity) { + Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), + Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Ok(table) => { table } } } diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index e9a150f34a5..be88f4e268a 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -438,6 +438,9 @@ pub use self::hash_set::HashSet; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc::range; +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +pub use alloc::allocator::CollectionAllocErr; + mod hash; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index da15941374d..ccc5373acc7 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -314,6 +314,7 @@ #![feature(thread_local)] #![feature(toowned_clone_into)] #![feature(try_from)] +#![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(unicode)] #![feature(untagged_unions)] diff --git a/src/test/ui/feature-gate-try_reserve.rs b/src/test/ui/feature-gate-try_reserve.rs new file mode 100644 index 00000000000..9322dbd272f --- /dev/null +++ b/src/test/ui/feature-gate-try_reserve.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + let v = Vec::new(); + v.try_reserve(10); //~ ERROR: use of unstable library feature 'try_reserve' +} diff --git a/src/test/ui/feature-gate-try_reserve.stderr b/src/test/ui/feature-gate-try_reserve.stderr new file mode 100644 index 00000000000..b1fef61dd24 --- /dev/null +++ b/src/test/ui/feature-gate-try_reserve.stderr @@ -0,0 +1,11 @@ +error[E0658]: use of unstable library feature 'try_reserve': new API (see issue #48043) + --> $DIR/feature-gate-try_reserve.rs:13:7 + | +LL | v.try_reserve(10); //~ ERROR: use of unstable library feature 'try_reserve' + | ^^^^^^^^^^^ + | + = help: add #![feature(try_reserve)] to the crate attributes to enable + +error: aborting due to previous error + +If you want more information on this error, try using "rustc --explain E0658" -- cgit 1.4.1-3-g733a5 From b08b5ae0ec22e67d1cab7495865a0b34d4e6c5a2 Mon Sep 17 00:00:00 2001 From: snf Date: Tue, 13 Mar 2018 03:41:45 -0700 Subject: try_reserve: disabling tests for asmjs, blocked by #48968 --- src/liballoc/tests/string.rs | 5 +++++ src/liballoc/tests/vec.rs | 7 ++++++- src/liballoc/tests/vec_deque.rs | 8 +++++++- src/libstd/collections/hash/map.rs | 4 ++++ 4 files changed, 22 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index d1e746ea43b..9bbba4e22b0 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -9,8 +9,11 @@ // except according to those terms. use std::borrow::Cow; +#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; +#[cfg(not(target_arch = "asmjs"))] use std::mem::size_of; +#[cfg(not(target_arch = "asmjs"))] use std::{usize, isize}; pub trait IntoCow<'a, B: ?Sized> where B: ToOwned { @@ -532,6 +535,7 @@ fn test_reserve_exact() { assert!(s.capacity() >= 33) } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -609,6 +613,7 @@ fn test_try_reserve() { } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 3c17a401bba..85e11d8b8ee 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -10,8 +10,11 @@ use std::borrow::Cow; use std::mem::size_of; -use std::{usize, isize, panic}; +use std::{usize, panic}; +#[cfg(not(target_arch = "asmjs"))] +use std::isize; use std::vec::{Drain, IntoIter}; +#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; struct DropCounter<'a> { @@ -991,6 +994,7 @@ fn test_reserve_exact() { assert!(v.capacity() >= 33) } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -1093,6 +1097,7 @@ fn test_try_reserve() { } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index fc1a0b624a5..9fd38ed6f6f 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -11,9 +11,13 @@ use std::collections::VecDeque; use std::fmt::Debug; use std::collections::vec_deque::{Drain}; +#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; +#[cfg(not(target_arch = "asmjs"))] use std::mem::size_of; -use std::{usize, isize}; +use std::isize; +#[cfg(not(target_arch = "asmjs"))] +use std::usize; use self::Taggy::*; use self::Taggypar::*; @@ -1049,6 +1053,7 @@ fn test_reserve_exact_2() { assert!(v.capacity() >= 48) } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -1150,6 +1155,7 @@ fn test_try_reserve() { } +#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index b18b38ec302..5f5dec2dd4f 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2755,8 +2755,11 @@ mod test_map { use cell::RefCell; use rand::{thread_rng, Rng}; use panic; + #[cfg(not(target_arch = "asmjs"))] use realstd::collections::CollectionAllocErr::*; + #[cfg(not(target_arch = "asmjs"))] use realstd::mem::size_of; + #[cfg(not(target_arch = "asmjs"))] use realstd::usize; #[test] @@ -3693,6 +3696,7 @@ mod test_map { assert_eq!(hm.len(), 0); } + #[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { -- cgit 1.4.1-3-g733a5 From b5913f2e7695ad247078619bf4c6a6d3dc4dece5 Mon Sep 17 00:00:00 2001 From: kennytm Date: Sun, 28 Jan 2018 03:09:36 +0800 Subject: Stabilize `inclusive_range` library feature. Stabilize std::ops::RangeInclusive and std::ops::RangeInclusiveTo. --- src/liballoc/lib.rs | 1 - src/liballoc/range.rs | 4 ++-- src/liballoc/string.rs | 8 ++++---- src/libcore/iter/range.rs | 12 ++++-------- src/libcore/ops/mod.rs | 2 +- src/libcore/ops/range.rs | 24 +++++++++--------------- src/libcore/slice/mod.rs | 4 ++-- src/libcore/str/mod.rs | 24 ++++++------------------ src/libcore/tests/lib.rs | 1 - src/librustc/lib.rs | 1 - src/librustc_mir/lib.rs | 1 - src/librustc_trans/lib.rs | 1 - src/libstd/lib.rs | 1 - src/test/compile-fail/range_inclusive_gate.rs | 21 --------------------- src/test/compile-fail/range_traits-1.rs | 2 -- src/test/compile-fail/range_traits-6.rs | 2 -- src/test/compile-fail/range_traits-7.rs | 2 +- src/test/parse-fail/range_inclusive.rs | 2 +- src/test/parse-fail/range_inclusive_dotdotdot.rs | 2 +- src/test/parse-fail/range_inclusive_gate.rs | 2 +- src/test/run-pass/range_inclusive.rs | 2 +- 21 files changed, 33 insertions(+), 86 deletions(-) delete mode 100644 src/test/compile-fail/range_inclusive_gate.rs (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3f306784558..cbfec554604 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -98,7 +98,6 @@ #![feature(fundamental)] #![feature(generic_param_attrs)] #![feature(i128_type)] -#![feature(inclusive_range)] #![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] diff --git a/src/liballoc/range.rs b/src/liballoc/range.rs index f862da0d61e..b03abc85180 100644 --- a/src/liballoc/range.rs +++ b/src/liballoc/range.rs @@ -103,7 +103,7 @@ impl RangeArgument for Range { } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl RangeArgument for RangeInclusive { fn start(&self) -> Bound<&T> { Included(&self.start) @@ -113,7 +113,7 @@ impl RangeArgument for RangeInclusive { } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl RangeArgument for RangeToInclusive { fn start(&self) -> Bound<&T> { Unbounded diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 370fb6b4e89..185fb61ae9e 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1876,7 +1876,7 @@ impl ops::Index for String { unsafe { str::from_utf8_unchecked(&self.vec) } } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::Index> for String { type Output = str; @@ -1885,7 +1885,7 @@ impl ops::Index> for String { Index::index(&**self, index) } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::Index> for String { type Output = str; @@ -1923,14 +1923,14 @@ impl ops::IndexMut for String { unsafe { str::from_utf8_unchecked_mut(&mut *self.vec) } } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::IndexMut> for String { #[inline] fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { IndexMut::index_mut(&mut **self, index) } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::IndexMut> for String { #[inline] fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { diff --git a/src/libcore/iter/range.rs b/src/libcore/iter/range.rs index 9a3fd215dcf..8d1080bb876 100644 --- a/src/libcore/iter/range.rs +++ b/src/libcore/iter/range.rs @@ -186,9 +186,7 @@ macro_rules! range_exact_iter_impl { macro_rules! range_incl_exact_iter_impl { ($($t:ty)*) => ($( - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl ExactSizeIterator for ops::RangeInclusive<$t> { } )*) } @@ -202,9 +200,7 @@ macro_rules! range_trusted_len_impl { macro_rules! range_incl_trusted_len_impl { ($($t:ty)*) => ($( - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] unsafe impl TrustedLen for ops::RangeInclusive<$t> { } )*) } @@ -328,7 +324,7 @@ impl FusedIterator for ops::RangeFrom {} #[unstable(feature = "trusted_len", issue = "37572")] unsafe impl TrustedLen for ops::RangeFrom {} -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl Iterator for ops::RangeInclusive { type Item = A; @@ -422,7 +418,7 @@ impl Iterator for ops::RangeInclusive { } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl DoubleEndedIterator for ops::RangeInclusive { #[inline] fn next_back(&mut self) -> Option { diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 70ef4487334..234970a81fa 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -191,7 +191,7 @@ pub use self::index::{Index, IndexMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] pub use self::range::{RangeInclusive, RangeToInclusive}; #[unstable(feature = "try_trait", issue = "42327")] diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 1d9c0f873b3..9bdd8094f61 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -283,7 +283,7 @@ impl> RangeTo { /// # Examples /// /// ``` -/// #![feature(inclusive_range,inclusive_range_syntax)] +/// #![feature(inclusive_range_syntax)] /// /// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); @@ -293,21 +293,17 @@ impl> RangeTo { /// assert_eq!(arr[1..=2], [ 1,2 ]); // RangeInclusive /// ``` #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeInclusive { /// The lower bound of the range (inclusive). - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] pub start: Idx, /// The upper bound of the range (inclusive). - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] pub end: Idx, } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "{:?}..={:?}", self.start, self.end) @@ -385,7 +381,7 @@ impl> RangeInclusive { /// The `..=end` syntax is a `RangeToInclusive`: /// /// ``` -/// #![feature(inclusive_range,inclusive_range_syntax)] +/// #![feature(inclusive_range_syntax)] /// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 }); /// ``` /// @@ -417,16 +413,14 @@ impl> RangeInclusive { /// [`Iterator`]: ../iter/trait.IntoIterator.html /// [slicing index]: ../slice/trait.SliceIndex.html #[derive(Copy, Clone, PartialEq, Eq, Hash)] -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeToInclusive { /// The upper bound of the range (inclusive) - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] pub end: Idx, } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeToInclusive { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { write!(fmt, "..={:?}", self.end) diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 19fe4dd36b6..0f1b7cb8fcc 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1039,7 +1039,7 @@ impl SliceIndex<[T]> for ops::RangeFull { } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl SliceIndex<[T]> for ops::RangeInclusive { type Output = [T]; @@ -1080,7 +1080,7 @@ impl SliceIndex<[T]> for ops::RangeInclusive { } } -#[unstable(feature = "inclusive_range", reason = "recently added, follows RFC", issue = "28237")] +#[stable(feature = "inclusive_range", since = "1.26.0")] impl SliceIndex<[T]> for ops::RangeToInclusive { type Output = [T]; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index e225c9522bc..9cf862bd936 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1779,9 +1779,7 @@ mod traits { } } - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::Index> for str { type Output = str; @@ -1791,9 +1789,7 @@ mod traits { } } - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::Index> for str { type Output = str; @@ -1803,18 +1799,14 @@ mod traits { } } - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::IndexMut> for str { #[inline] fn index_mut(&mut self, index: ops::RangeInclusive) -> &mut str { index.index_mut(self) } } - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl ops::IndexMut> for str { #[inline] fn index_mut(&mut self, index: ops::RangeToInclusive) -> &mut str { @@ -1997,9 +1989,7 @@ mod traits { } } - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl SliceIndex for ops::RangeInclusive { type Output = str; #[inline] @@ -2042,9 +2032,7 @@ mod traits { - #[unstable(feature = "inclusive_range", - reason = "recently added, follows RFC", - issue = "28237")] + #[stable(feature = "inclusive_range", since = "1.26.0")] impl SliceIndex for ops::RangeToInclusive { type Output = str; #[inline] diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 5bd5bca19c4..8f01fbeb30e 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -23,7 +23,6 @@ #![feature(fmt_internals)] #![feature(iterator_step_by)] #![feature(i128_type)] -#![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(iterator_try_fold)] #![feature(iterator_flatten)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 51882385b2e..149ea96b636 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -54,7 +54,6 @@ #![feature(fs_read_write)] #![feature(i128)] #![feature(i128_type)] -#![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![cfg_attr(windows, feature(libc))] #![feature(match_default_bindings)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 5510e219780..31eb203eefe 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -29,7 +29,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(fs_read_write)] #![feature(i128_type)] #![feature(inclusive_range_syntax)] -#![feature(inclusive_range)] #![feature(macro_vis_matcher)] #![feature(match_default_bindings)] #![feature(exhaustive_patterns)] diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 39eb38658fe..a9334461825 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -26,7 +26,6 @@ #![allow(unused_attributes)] #![feature(i128_type)] #![feature(i128)] -#![feature(inclusive_range)] #![feature(inclusive_range_syntax)] #![feature(libc)] #![feature(quote)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index eea0e6b6752..c71a562b0d1 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -271,7 +271,6 @@ #![feature(heap_api)] #![feature(i128)] #![feature(i128_type)] -#![feature(inclusive_range)] #![feature(int_error_internals)] #![feature(integer_atomics)] #![feature(into_cow)] diff --git a/src/test/compile-fail/range_inclusive_gate.rs b/src/test/compile-fail/range_inclusive_gate.rs deleted file mode 100644 index 5b063dc1137..00000000000 --- a/src/test/compile-fail/range_inclusive_gate.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Make sure that #![feature(inclusive_range)] is required. - -#![feature(inclusive_range_syntax)] -// #![feature(inclusive_range)] - -pub fn main() { - let _: std::ops::RangeInclusive<_> = { use std::intrinsics; 1 } ..= { use std::intrinsics; 2 }; - //~^ ERROR use of unstable library feature 'inclusive_range' - //~| ERROR core_intrinsics - //~| ERROR core_intrinsics -} diff --git a/src/test/compile-fail/range_traits-1.rs b/src/test/compile-fail/range_traits-1.rs index f1ea8b04e5a..7645dbb1a6d 100644 --- a/src/test/compile-fail/range_traits-1.rs +++ b/src/test/compile-fail/range_traits-1.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(inclusive_range)] - use std::ops::*; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] diff --git a/src/test/compile-fail/range_traits-6.rs b/src/test/compile-fail/range_traits-6.rs index 7c62711feae..f9510b5061c 100644 --- a/src/test/compile-fail/range_traits-6.rs +++ b/src/test/compile-fail/range_traits-6.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(inclusive_range)] - use std::ops::*; #[derive(Copy, Clone)] //~ ERROR Copy diff --git a/src/test/compile-fail/range_traits-7.rs b/src/test/compile-fail/range_traits-7.rs index b6fec773a77..871b55b85cf 100644 --- a/src/test/compile-fail/range_traits-7.rs +++ b/src/test/compile-fail/range_traits-7.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(rustc_attrs, inclusive_range)] +#![feature(rustc_attrs)] use std::ops::*; diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs index cc32b9903b5..6c6caa1e649 100644 --- a/src/test/parse-fail/range_inclusive.rs +++ b/src/test/parse-fail/range_inclusive.rs @@ -10,7 +10,7 @@ // Make sure that inclusive ranges with no end point don't parse. -#![feature(inclusive_range_syntax, inclusive_range)] +#![feature(inclusive_range_syntax)] pub fn main() { for _ in 1..= {} //~ERROR inclusive range with no end diff --git a/src/test/parse-fail/range_inclusive_dotdotdot.rs b/src/test/parse-fail/range_inclusive_dotdotdot.rs index a4c36a2f0ba..8a24038638b 100644 --- a/src/test/parse-fail/range_inclusive_dotdotdot.rs +++ b/src/test/parse-fail/range_inclusive_dotdotdot.rs @@ -12,7 +12,7 @@ // Make sure that inclusive ranges with `...` syntax don't parse. -#![feature(inclusive_range_syntax, inclusive_range)] +#![feature(inclusive_range_syntax)] use std::ops::RangeToInclusive; diff --git a/src/test/parse-fail/range_inclusive_gate.rs b/src/test/parse-fail/range_inclusive_gate.rs index 6b6afc504e1..c8c84000e41 100644 --- a/src/test/parse-fail/range_inclusive_gate.rs +++ b/src/test/parse-fail/range_inclusive_gate.rs @@ -12,7 +12,7 @@ // Make sure that #![feature(inclusive_range_syntax)] is required. -// #![feature(inclusive_range_syntax, inclusive_range)] +// #![feature(inclusive_range_syntax)] macro_rules! m { () => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index 71e11804052..29947860ff6 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -10,7 +10,7 @@ // Test inclusive range syntax. -#![feature(inclusive_range_syntax, inclusive_range, iterator_step_by)] +#![feature(inclusive_range_syntax, iterator_step_by)] use std::ops::{RangeInclusive, RangeToInclusive}; -- cgit 1.4.1-3-g733a5 From 92d1f8d8e4be62e6f4dc32bc57f9d44f53117ec9 Mon Sep 17 00:00:00 2001 From: kennytm Date: Sun, 28 Jan 2018 03:19:29 +0800 Subject: Stabilize `inclusive_range_syntax` language feature. Stabilize the syntax `a..=b` and `..=b`. --- .../language-features/inclusive-range-syntax.md | 20 ------ src/liballoc/tests/lib.rs | 2 +- src/libcore/lib.rs | 2 +- src/libcore/ops/range.rs | 19 ++---- src/libcore/tests/lib.rs | 2 +- src/librustc/lib.rs | 2 +- src/librustc_incremental/lib.rs | 2 +- src/librustc_mir/lib.rs | 2 +- src/librustc_trans/lib.rs | 2 +- src/libsyntax/diagnostic_list.rs | 4 -- src/libsyntax/feature_gate.rs | 10 +-- .../incremental/hashes/indexing_expressions.rs | 1 - src/test/parse-fail/range_inclusive.rs | 2 - src/test/parse-fail/range_inclusive_dotdotdot.rs | 2 - src/test/parse-fail/range_inclusive_gate.rs | 74 ---------------------- src/test/run-pass/range_inclusive.rs | 2 +- src/test/run-pass/range_inclusive_gate.rs | 3 +- src/test/ui/impossible_range.rs | 2 - src/test/ui/impossible_range.stderr | 4 +- 19 files changed, 19 insertions(+), 138 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/inclusive-range-syntax.md delete mode 100644 src/test/parse-fail/range_inclusive_gate.rs (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md b/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md deleted file mode 100644 index 56f58803150..00000000000 --- a/src/doc/unstable-book/src/language-features/inclusive-range-syntax.md +++ /dev/null @@ -1,20 +0,0 @@ -# `inclusive_range_syntax` - -The tracking issue for this feature is: [#28237] - -[#28237]: https://github.com/rust-lang/rust/issues/28237 - ------------------------- - -To get a range that goes from 0 to 10 and includes the value 10, you -can write `0..=10`: - -```rust -#![feature(inclusive_range_syntax)] - -fn main() { - for i in 0..=10 { - println!("{}", i); - } -} -``` diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 168dbb2ce9b..f5deecd47e8 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -14,7 +14,7 @@ #![feature(alloc_system)] #![feature(attr_literals)] #![feature(box_syntax)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(collection_placement)] #![feature(const_fn)] #![feature(drain_filter)] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index a947c9f0b7c..9aebe2e4ee4 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -79,7 +79,7 @@ #![feature(fn_must_use)] #![feature(fundamental)] #![feature(i128_type)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(intrinsics)] #![feature(iterator_flatten)] #![feature(iterator_repeat_with)] diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 9bdd8094f61..32aa6508805 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -128,7 +128,7 @@ impl> Range { /// The range is empty if either side is incomparable: /// /// ``` - /// #![feature(range_is_empty,inclusive_range_syntax)] + /// #![feature(range_is_empty)] /// /// use std::f32::NAN; /// assert!(!(3.0..5.0).is_empty()); @@ -283,8 +283,6 @@ impl> RangeTo { /// # Examples /// /// ``` -/// #![feature(inclusive_range_syntax)] -/// /// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// @@ -316,7 +314,7 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// #![feature(range_contains,inclusive_range_syntax)] + /// #![feature(range_contains)] /// /// assert!(!(3..=5).contains(2)); /// assert!( (3..=5).contains(3)); @@ -337,7 +335,7 @@ impl> RangeInclusive { /// # Examples /// /// ``` - /// #![feature(range_is_empty,inclusive_range_syntax)] + /// #![feature(range_is_empty)] /// /// assert!(!(3..=5).is_empty()); /// assert!(!(3..=3).is_empty()); @@ -347,7 +345,7 @@ impl> RangeInclusive { /// The range is empty if either side is incomparable: /// /// ``` - /// #![feature(range_is_empty,inclusive_range_syntax)] + /// #![feature(range_is_empty)] /// /// use std::f32::NAN; /// assert!(!(3.0..=5.0).is_empty()); @@ -358,7 +356,7 @@ impl> RangeInclusive { /// This method returns `true` after iteration has finished: /// /// ``` - /// #![feature(range_is_empty,inclusive_range_syntax)] + /// #![feature(range_is_empty)] /// /// let mut r = 3..=5; /// for _ in r.by_ref() {} @@ -381,7 +379,6 @@ impl> RangeInclusive { /// The `..=end` syntax is a `RangeToInclusive`: /// /// ``` -/// #![feature(inclusive_range_syntax)] /// assert_eq!((..=5), std::ops::RangeToInclusive{ end: 5 }); /// ``` /// @@ -389,8 +386,6 @@ impl> RangeInclusive { /// `for` loop directly. This won't compile: /// /// ```compile_fail,E0277 -/// #![feature(inclusive_range_syntax)] -/// /// // error[E0277]: the trait bound `std::ops::RangeToInclusive<{integer}>: /// // std::iter::Iterator` is not satisfied /// for i in ..=5 { @@ -402,8 +397,6 @@ impl> RangeInclusive { /// array elements up to and including the index indicated by `end`. /// /// ``` -/// #![feature(inclusive_range_syntax)] -/// /// let arr = [0, 1, 2, 3]; /// assert_eq!(arr[ ..=2], [0,1,2 ]); // RangeToInclusive /// assert_eq!(arr[1..=2], [ 1,2 ]); @@ -434,7 +427,7 @@ impl> RangeToInclusive { /// # Examples /// /// ``` - /// #![feature(range_contains,inclusive_range_syntax)] + /// #![feature(range_contains)] /// /// assert!( (..=5).contains(-1_000_000_000)); /// assert!( (..=5).contains(5)); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 8f01fbeb30e..85787f38f06 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -23,7 +23,7 @@ #![feature(fmt_internals)] #![feature(iterator_step_by)] #![feature(i128_type)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(iterator_try_fold)] #![feature(iterator_flatten)] #![feature(conservative_impl_trait)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 149ea96b636..ff2e8ea79d3 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -54,7 +54,7 @@ #![feature(fs_read_write)] #![feature(i128)] #![feature(i128_type)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![cfg_attr(windows, feature(libc))] #![feature(match_default_bindings)] #![feature(macro_lifetime_matcher)] diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 65fbd9d0bf8..d7ccf9d5562 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -18,7 +18,7 @@ #![feature(conservative_impl_trait)] #![feature(fs_read_write)] #![feature(i128_type)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(specialization)] extern crate graphviz; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 31eb203eefe..ad7a5c94022 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -28,7 +28,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(dyn_trait)] #![feature(fs_read_write)] #![feature(i128_type)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(macro_vis_matcher)] #![feature(match_default_bindings)] #![feature(exhaustive_patterns)] diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index a9334461825..f2b76eb57d6 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -26,7 +26,7 @@ #![allow(unused_attributes)] #![feature(i128_type)] #![feature(i128)] -#![feature(inclusive_range_syntax)] +#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(libc)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/libsyntax/diagnostic_list.rs b/src/libsyntax/diagnostic_list.rs index 549ef88afcc..1f87c1b94c5 100644 --- a/src/libsyntax/diagnostic_list.rs +++ b/src/libsyntax/diagnostic_list.rs @@ -218,8 +218,6 @@ An inclusive range was used with no end. Erroneous code example: ```compile_fail,E0586 -#![feature(inclusive_range_syntax)] - fn main() { let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1]; let x = &tmp[1..=]; // error: inclusive range was used with no end @@ -239,8 +237,6 @@ fn main() { Or put an end to your inclusive range: ``` -#![feature(inclusive_range_syntax)] - fn main() { let tmp = vec![0, 1, 2, 3, 4, 4, 3, 3, 2, 1]; let x = &tmp[1..=3]; // ok! diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 91364fe6ed4..a415117b599 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -268,9 +268,6 @@ declare_features! ( // rustc internal (active, abi_vectorcall, "1.7.0", None, None), - // a..=b and ..=b - (active, inclusive_range_syntax, "1.7.0", Some(28237), None), - // X..Y patterns (active, exclusive_range_pattern, "1.11.0", Some(37854), None), @@ -554,6 +551,8 @@ declare_features! ( (accepted, match_beginning_vert, "1.25.0", Some(44101), None), // Nested groups in `use` (RFC 2128) (accepted, use_nested_groups, "1.25.0", Some(44494), None), + // a..=b and ..=b + (accepted, inclusive_range_syntax, "1.26.0", Some(28237), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1592,11 +1591,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, type_ascription, e.span, "type ascription is experimental"); } - ast::ExprKind::Range(_, _, ast::RangeLimits::Closed) => { - gate_feature_post!(&self, inclusive_range_syntax, - e.span, - "inclusive range syntax is experimental"); - } ast::ExprKind::InPlace(..) => { gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN); } diff --git a/src/test/incremental/hashes/indexing_expressions.rs b/src/test/incremental/hashes/indexing_expressions.rs index e66e239b33c..fb63aa857aa 100644 --- a/src/test/incremental/hashes/indexing_expressions.rs +++ b/src/test/incremental/hashes/indexing_expressions.rs @@ -23,7 +23,6 @@ #![allow(warnings)] #![feature(rustc_attrs)] #![crate_type="rlib"] -#![feature(inclusive_range_syntax)] // Change simple index --------------------------------------------------------- #[cfg(cfail1)] diff --git a/src/test/parse-fail/range_inclusive.rs b/src/test/parse-fail/range_inclusive.rs index 6c6caa1e649..2aa7d6d6cd7 100644 --- a/src/test/parse-fail/range_inclusive.rs +++ b/src/test/parse-fail/range_inclusive.rs @@ -10,8 +10,6 @@ // Make sure that inclusive ranges with no end point don't parse. -#![feature(inclusive_range_syntax)] - pub fn main() { for _ in 1..= {} //~ERROR inclusive range with no end //~^HELP bounded at the end diff --git a/src/test/parse-fail/range_inclusive_dotdotdot.rs b/src/test/parse-fail/range_inclusive_dotdotdot.rs index 8a24038638b..fa6474717d3 100644 --- a/src/test/parse-fail/range_inclusive_dotdotdot.rs +++ b/src/test/parse-fail/range_inclusive_dotdotdot.rs @@ -12,8 +12,6 @@ // Make sure that inclusive ranges with `...` syntax don't parse. -#![feature(inclusive_range_syntax)] - use std::ops::RangeToInclusive; fn return_range_to() -> RangeToInclusive { diff --git a/src/test/parse-fail/range_inclusive_gate.rs b/src/test/parse-fail/range_inclusive_gate.rs deleted file mode 100644 index c8c84000e41..00000000000 --- a/src/test/parse-fail/range_inclusive_gate.rs +++ /dev/null @@ -1,74 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// gate-test-inclusive_range_syntax - -// Make sure that #![feature(inclusive_range_syntax)] is required. - -// #![feature(inclusive_range_syntax)] - -macro_rules! m { - () => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental -} - -#[cfg(nope)] -fn f() {} -#[cfg(not(nope))] -fn f() { - for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental -} - -#[cfg(nope)] -macro_rules! n { () => {} } -#[cfg(not(nope))] -macro_rules! n { - () => { for _ in 1..=10 {} } //~ ERROR inclusive range syntax is experimental -} - -macro_rules! o { - () => {{ - #[cfg(nope)] - fn g() {} - #[cfg(not(nope))] - fn g() { - for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental - } - - g(); - }} -} - -#[cfg(nope)] -macro_rules! p { () => {} } -#[cfg(not(nope))] -macro_rules! p { - () => {{ - #[cfg(nope)] - fn h() {} - #[cfg(not(nope))] - fn h() { - for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental - } - - h(); - }} -} - -pub fn main() { - for _ in 1..=10 {} //~ ERROR inclusive range syntax is experimental - for _ in ..=10 {} //~ ERROR inclusive range syntax is experimental - - f(); // not allowed in cfg'ed functions - - m!(); // not allowed in macros - n!(); // not allowed in cfg'ed macros - o!(); // not allowed in macros that output cfgs - p!(); // not allowed in cfg'ed macros that output cfgs -} diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index 29947860ff6..5d46bfab887 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -10,7 +10,7 @@ // Test inclusive range syntax. -#![feature(inclusive_range_syntax, iterator_step_by)] +#![feature(iterator_step_by)] use std::ops::{RangeInclusive, RangeToInclusive}; diff --git a/src/test/run-pass/range_inclusive_gate.rs b/src/test/run-pass/range_inclusive_gate.rs index 570087aedbb..6c2731fa5a9 100644 --- a/src/test/run-pass/range_inclusive_gate.rs +++ b/src/test/run-pass/range_inclusive_gate.rs @@ -9,8 +9,7 @@ // except according to those terms. // Test that you only need the syntax gate if you don't mention the structs. - -#![feature(inclusive_range_syntax)] +// (Obsoleted since both features are stabilized) fn main() { let mut count = 0; diff --git a/src/test/ui/impossible_range.rs b/src/test/ui/impossible_range.rs index 5c72c506e6b..073ed867bdb 100644 --- a/src/test/ui/impossible_range.rs +++ b/src/test/ui/impossible_range.rs @@ -10,8 +10,6 @@ // Make sure that invalid ranges generate an error during HIR lowering, not an ICE -#![feature(inclusive_range_syntax)] - pub fn main() { ..; 0..; diff --git a/src/test/ui/impossible_range.stderr b/src/test/ui/impossible_range.stderr index d941b522def..cfeaa53a6bb 100644 --- a/src/test/ui/impossible_range.stderr +++ b/src/test/ui/impossible_range.stderr @@ -1,5 +1,5 @@ error[E0586]: inclusive range with no end - --> $DIR/impossible_range.rs:20:8 + --> $DIR/impossible_range.rs:18:8 | LL | ..=; //~ERROR inclusive range with no end | ^ @@ -7,7 +7,7 @@ LL | ..=; //~ERROR inclusive range with no end = help: inclusive ranges must be bounded at the end (`..=b` or `a..=b`) error[E0586]: inclusive range with no end - --> $DIR/impossible_range.rs:27:9 + --> $DIR/impossible_range.rs:25:9 | LL | 0..=; //~ERROR inclusive range with no end | ^ -- cgit 1.4.1-3-g733a5 From 939cfa251aeb34b4b1a11396af1a3396792c708d Mon Sep 17 00:00:00 2001 From: kennytm Date: Thu, 15 Mar 2018 02:50:55 +0800 Subject: Keep the fields of RangeInclusive unstable. --- src/liballoc/lib.rs | 1 + src/liballoc/tests/lib.rs | 1 + src/libcore/ops/range.rs | 6 ++++-- src/libcore/tests/lib.rs | 1 + src/librustc/lib.rs | 1 + src/librustc_mir/lib.rs | 1 + src/librustc_trans/lib.rs | 1 + 7 files changed, 10 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index cbfec554604..fb7f114ba1a 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -123,6 +123,7 @@ #![feature(on_unimplemented)] #![feature(exact_chunks)] #![feature(pointer_methods)] +#![feature(inclusive_range_fields)] #![cfg_attr(not(test), feature(fn_traits, placement_new_protocol, swap_with_slice, i128))] #![cfg_attr(test, feature(test, box_heap))] diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index f5deecd47e8..4fea2375558 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -29,6 +29,7 @@ #![feature(unboxed_closures)] #![feature(unicode)] #![feature(exact_chunks)] +#![feature(inclusive_range_fields)] extern crate alloc_system; extern crate std_unicode; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 32aa6508805..be51f5239b0 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -283,6 +283,8 @@ impl> RangeTo { /// # Examples /// /// ``` +/// #![feature(inclusive_range_fields)] +/// /// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 }); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// @@ -294,10 +296,10 @@ impl> RangeTo { #[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeInclusive { /// The lower bound of the range (inclusive). - #[stable(feature = "inclusive_range", since = "1.26.0")] + #[unstable(feature = "inclusive_range_fields", issue = "49022")] pub start: Idx, /// The upper bound of the range (inclusive). - #[stable(feature = "inclusive_range", since = "1.26.0")] + #[unstable(feature = "inclusive_range_fields", issue = "49022")] pub end: Idx, } diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 85787f38f06..e53964b5769 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -47,6 +47,7 @@ #![feature(exact_chunks)] #![feature(atomic_nand)] #![feature(reverse_bits)] +#![feature(inclusive_range_fields)] extern crate core; extern crate test; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ff2e8ea79d3..77259f156e5 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -75,6 +75,7 @@ #![feature(trusted_len)] #![feature(catch_expr)] #![feature(test)] +#![feature(inclusive_range_fields)] #![recursion_limit="512"] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ad7a5c94022..ff35412ea5b 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -39,6 +39,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(nonzero)] #![feature(underscore_lifetimes)] #![cfg_attr(stage0, feature(never_type))] +#![feature(inclusive_range_fields)] extern crate arena; #[macro_use] diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index f2b76eb57d6..9b09dbf5276 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -33,6 +33,7 @@ #![feature(slice_patterns)] #![feature(conservative_impl_trait)] #![feature(optin_builtin_traits)] +#![feature(inclusive_range_fields)] use rustc::dep_graph::WorkProduct; use syntax_pos::symbol::Symbol; -- cgit 1.4.1-3-g733a5 From 9e64946bded0698c8c45d74526e6a8b5a514c4a1 Mon Sep 17 00:00:00 2001 From: snf Date: Wed, 14 Mar 2018 14:56:37 +0000 Subject: setting ABORTING_MALLOC for asmjs backend --- src/liballoc/tests/string.rs | 5 ----- src/liballoc/tests/vec.rs | 7 +------ src/liballoc/tests/vec_deque.rs | 8 +------- src/librustc_back/target/asmjs_unknown_emscripten.rs | 4 +++- src/libstd/collections/hash/map.rs | 4 ---- 5 files changed, 5 insertions(+), 23 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 9bbba4e22b0..d1e746ea43b 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -9,11 +9,8 @@ // except according to those terms. use std::borrow::Cow; -#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; -#[cfg(not(target_arch = "asmjs"))] use std::mem::size_of; -#[cfg(not(target_arch = "asmjs"))] use std::{usize, isize}; pub trait IntoCow<'a, B: ?Sized> where B: ToOwned { @@ -535,7 +532,6 @@ fn test_reserve_exact() { assert!(s.capacity() >= 33) } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -613,7 +609,6 @@ fn test_try_reserve() { } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 85e11d8b8ee..3c17a401bba 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -10,11 +10,8 @@ use std::borrow::Cow; use std::mem::size_of; -use std::{usize, panic}; -#[cfg(not(target_arch = "asmjs"))] -use std::isize; +use std::{usize, isize, panic}; use std::vec::{Drain, IntoIter}; -#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; struct DropCounter<'a> { @@ -994,7 +991,6 @@ fn test_reserve_exact() { assert!(v.capacity() >= 33) } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -1097,7 +1093,6 @@ fn test_try_reserve() { } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index 9fd38ed6f6f..fc1a0b624a5 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -11,13 +11,9 @@ use std::collections::VecDeque; use std::fmt::Debug; use std::collections::vec_deque::{Drain}; -#[cfg(not(target_arch = "asmjs"))] use std::collections::CollectionAllocErr::*; -#[cfg(not(target_arch = "asmjs"))] use std::mem::size_of; -use std::isize; -#[cfg(not(target_arch = "asmjs"))] -use std::usize; +use std::{usize, isize}; use self::Taggy::*; use self::Taggypar::*; @@ -1053,7 +1049,6 @@ fn test_reserve_exact_2() { assert!(v.capacity() >= 48) } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { @@ -1155,7 +1150,6 @@ fn test_try_reserve() { } -#[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve_exact() { diff --git a/src/librustc_back/target/asmjs_unknown_emscripten.rs b/src/librustc_back/target/asmjs_unknown_emscripten.rs index f114926740a..ab7df4ba1c5 100644 --- a/src/librustc_back/target/asmjs_unknown_emscripten.rs +++ b/src/librustc_back/target/asmjs_unknown_emscripten.rs @@ -15,7 +15,9 @@ pub fn target() -> Result { let mut args = LinkArgs::new(); args.insert(LinkerFlavor::Em, vec!["-s".to_string(), - "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string()]); + "ERROR_ON_UNDEFINED_SYMBOLS=1".to_string(), + "-s".to_string(), + "ABORTING_MALLOC=0".to_string()]); let opts = TargetOptions { dynamic_linking: false, diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 5f5dec2dd4f..b18b38ec302 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2755,11 +2755,8 @@ mod test_map { use cell::RefCell; use rand::{thread_rng, Rng}; use panic; - #[cfg(not(target_arch = "asmjs"))] use realstd::collections::CollectionAllocErr::*; - #[cfg(not(target_arch = "asmjs"))] use realstd::mem::size_of; - #[cfg(not(target_arch = "asmjs"))] use realstd::usize; #[test] @@ -3696,7 +3693,6 @@ mod test_map { assert_eq!(hm.len(), 0); } - #[cfg(not(target_arch = "asmjs"))] #[test] fn test_try_reserve() { -- cgit 1.4.1-3-g733a5 From e3c5f6958f3fc82858a3674277d620d3ba844350 Mon Sep 17 00:00:00 2001 From: boats Date: Thu, 15 Mar 2018 12:55:37 -0700 Subject: Add liballoc APIs. --- src/liballoc/boxed.rs | 97 +++++++++++++++++++++++++++++++++++++++++++++++++-- src/liballoc/lib.rs | 1 + 2 files changed, 96 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 75a59de337c..42903c7bde0 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -64,8 +64,8 @@ use core::cmp::Ordering; use core::fmt; use core::hash::{self, Hash, Hasher}; use core::iter::FusedIterator; -use core::marker::{self, Unsize}; -use core::mem; +use core::marker::{self, Unpin, Unsize}; +use core::mem::{self, Pin}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer}; use core::ptr::{self, NonNull, Unique}; @@ -896,3 +896,96 @@ impl Generator for Box (**self).resume() } } + +/// A pinned, heap allocated reference. +#[unstable(feature = "pin", issue = "0")] +pub struct PinBox { + inner: Box, +} + +#[unstable(feature = "pin", issue = "0")] +impl PinBox { + /// Allocate memory on the heap, move the data into it and pin it. + #[unstable(feature = "pin", issue = "0")] + pub fn new(data: T) -> PinBox { + PinBox { inner: Box::new(data) } + } +} + +#[unstable(feature = "pin", issue = "0")] +impl PinBox { + /// Get a pinned reference to the data in this PinBox. + pub fn as_pin<'a>(&'a mut self) -> Pin<'a, T> { + unsafe { Pin::new_unchecked(&mut *self.inner) } + } + + /// Get a mutable reference to the data inside this PinBox. + /// + /// This function is unsafe. Users must guarantee that the data is never + /// moved out of this reference. + pub unsafe fn get_mut<'a>(this: &'a mut PinBox) -> &'a mut T { + &mut *this.inner + } + + /// Convert this PinBox into an unpinned Box. + /// + /// This function is unsafe. Users must guarantee that the data is never + /// moved out of the box. + pub unsafe fn unpin(this: PinBox) -> Box { + this.inner + } +} + +#[unstable(feature = "pin", issue = "0")] +impl From> for PinBox { + fn from(boxed: Box) -> PinBox { + PinBox { inner: boxed } + } +} + +#[unstable(feature = "pin", issue = "0")] +impl From> for Box { + fn from(pinned: PinBox) -> Box { + pinned.inner + } +} + +#[unstable(feature = "pin", issue = "0")] +impl Deref for PinBox { + type Target = T; + + fn deref(&self) -> &T { + &*self.inner + } +} + +#[unstable(feature = "pin", issue = "0")] +impl DerefMut for PinBox { + fn deref_mut(&mut self) -> &mut T { + &mut *self.inner + } +} + +#[unstable(feature = "pin", issue = "0")] +impl fmt::Display for PinBox { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&*self.inner, f) + } +} + +#[unstable(feature = "pin", issue = "0")] +impl fmt::Debug for PinBox { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&*self.inner, f) + } +} + +#[unstable(feature = "pin", issue = "0")] +impl fmt::Pointer for PinBox { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // It's not possible to extract the inner Uniq directly from the Box, + // instead we cast it to a *const which aliases the Unique + let ptr: *const T = &*self.inner; + fmt::Pointer::fmt(&ptr, f) + } +} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d250cfe1880..73e452b6a36 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -107,6 +107,7 @@ #![feature(offset_to)] #![feature(optin_builtin_traits)] #![feature(pattern)] +#![feature(pin)] #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(ptr_internals)] -- cgit 1.4.1-3-g733a5 From 2f1c24a60d173f323fdbe3c716349a9974134568 Mon Sep 17 00:00:00 2001 From: boats Date: Thu, 15 Mar 2018 16:10:18 -0700 Subject: CoerceUnsized for PinBox --- src/liballoc/boxed.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 42903c7bde0..5e5d7b91720 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -989,3 +989,6 @@ impl fmt::Pointer for PinBox { fmt::Pointer::fmt(&ptr, f) } } + +#[unstable(feature = "pin", issue = "0")] +impl, U: ?Sized> CoerceUnsized> for PinBox {} -- cgit 1.4.1-3-g733a5 From 81d0ecef2c4ba5ebb36a72f76adbce1b229fb856 Mon Sep 17 00:00:00 2001 From: boats Date: Thu, 15 Mar 2018 16:16:11 -0700 Subject: Pin and PinBox are fundamental. --- src/liballoc/boxed.rs | 1 + src/libcore/mem.rs | 1 + 2 files changed, 2 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 5e5d7b91720..46d3ccb9de5 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -899,6 +899,7 @@ impl Generator for Box /// A pinned, heap allocated reference. #[unstable(feature = "pin", issue = "0")] +#[fundamental] pub struct PinBox { inner: Box, } diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 792d71732e6..e960b5ae758 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1112,6 +1112,7 @@ pub unsafe fn unreachable() -> ! { /// safe to move a value out of a pinned reference unless the type of that /// value implements the `Unpin` trait. #[unstable(feature = "pin", issue = "0")] +#[fundamental] pub struct Pin<'a, T: ?Sized + 'a> { inner: &'a mut T, } -- cgit 1.4.1-3-g733a5 From f0ad533fe316c4eb26ab6ebda879e60956633c0e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 16 Mar 2018 11:44:55 +0100 Subject: Remove deprecated unstable alloc::heap::EMPTY constant --- src/liballoc/heap.rs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 372d606e457..c13ad39e5e1 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -228,14 +228,6 @@ unsafe impl Alloc for Heap { } } -/// An arbitrary non-null address to represent zero-size allocations. -/// -/// This preserves the non-null invariant for types like `Box`. The address -/// may overlap with non-zero-size memory allocations. -#[rustc_deprecated(since = "1.19.0", reason = "Use Unique/NonNull::empty() instead")] -#[unstable(feature = "heap_api", issue = "27700")] -pub const EMPTY: *mut () = 1 as *mut (); - /// The allocator for unique pointers. // This function must not unwind. If it does, MIR trans will fail. #[cfg(not(test))] -- cgit 1.4.1-3-g733a5 From ea6a1bdf6ba754f3b96e3a46f9173b17ff18ed07 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 1 Mar 2018 10:46:06 +0000 Subject: Compute each key only one during slice::sort_by_key --- src/liballoc/slice.rs | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index dc40062ef13..c57f1e7f572 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1328,10 +1328,18 @@ impl [T] { /// ``` #[stable(feature = "slice_sort_by_key", since = "1.7.0")] #[inline] - pub fn sort_by_key(&mut self, mut f: F) + pub fn sort_by_key(&mut self, f: F) where F: FnMut(&T) -> B, B: Ord { - merge_sort(self, |a, b| f(a).lt(&f(b))); + let mut indices: Vec<_> = self.iter().map(f).enumerate().map(|(i, k)| (k, i)).collect(); + indices.sort(); + for i in 0..self.len() { + let mut index = indices[i].1; + while index < i { + index = indices[index].1; + } + self.swap(i, index); + } } /// Sorts the slice, but may not preserve the order of equal elements. -- cgit 1.4.1-3-g733a5 From 670e69e20753e2ef33ee61b0515092cace3fd716 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 1 Mar 2018 12:15:05 +0000 Subject: Update documentation for sort_by_key --- src/liballoc/slice.rs | 11 ++++------- 1 file changed, 4 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index c57f1e7f572..db3b14859dd 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1302,11 +1302,9 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case. - /// - /// When applicable, unstable sorting is preferred because it is generally faster than stable - /// sorting and it doesn't allocate auxiliary memory. - /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). + /// During sorting, the key function is called only once per element. + /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)` + /// worst-case, where the key function is `O(m)`. /// /// # Current implementation /// @@ -1315,8 +1313,7 @@ impl [T] { /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of /// two or more sorted sequences concatenated one after another. /// - /// Also, it allocates temporary storage half the size of `self`, but for short slices a - /// non-allocating insertion sort is used instead. + /// The algorithm allocates temporary storage the size of `self`. /// /// # Examples /// -- cgit 1.4.1-3-g733a5 From b8452cc3266f2d9567b5c07ca3044b3960e10ebf Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 1 Mar 2018 12:22:11 +0000 Subject: Clarify behaviour of sort_unstable_by_key with respect to sort_by_key --- src/liballoc/slice.rs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index db3b14859dd..63b22bd0f6d 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1303,6 +1303,7 @@ impl [T] { /// Sorts the slice with a key extraction function. /// /// During sorting, the key function is called only once per element. + /// /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)` /// worst-case, where the key function is `O(m)`. /// @@ -1329,7 +1330,10 @@ impl [T] { where F: FnMut(&T) -> B, B: Ord { let mut indices: Vec<_> = self.iter().map(f).enumerate().map(|(i, k)| (k, i)).collect(); - indices.sort(); + // The elements of `indices` are unique, as they are indexed, so any sort will be stable + // with respect to the original slice. We use `sort_unstable` here because it requires less + // memory allocation. + indices.sort_unstable(); for i in 0..self.len() { let mut index = indices[i].1; while index < i { @@ -1414,8 +1418,11 @@ impl [T] { /// Sorts the slice with a key extraction function, but may not preserve the order of equal /// elements. /// + /// Note that, currently, the key function for `sort_unstable_by_key` is called multiple times + /// per element, unlike `sort_stable_by_key`. + /// /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), - /// and `O(n log n)` worst-case. + /// and `O(m n log m n)` worst-case, where the key function is `O(m)`. /// /// # Current implementation /// @@ -1425,8 +1432,8 @@ impl [T] { /// randomization to avoid degenerate cases, but with a fixed seed to always provide /// deterministic behavior. /// - /// It is typically faster than stable sorting, except in a few special cases, e.g. when the - /// slice consists of several concatenated sorted sequences. + /// Due to its key calling strategy, `sort_unstable_by_key` is likely to be slower than + /// `sort_by_key` in cases where the key function is expensive. /// /// # Examples /// -- cgit 1.4.1-3-g733a5 From 9fbee359d7eb3a621604bc2067a375f6d4b757e5 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 1 Mar 2018 13:39:52 +0000 Subject: Add a test for sort_by_key --- src/liballoc/tests/slice.rs | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index d9e9d91cea8..300f6abaa7f 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -425,6 +425,11 @@ fn test_sort() { v.sort_by(|a, b| b.cmp(a)); assert!(v.windows(2).all(|w| w[0] >= w[1])); + // Sort in lexicographic order. + let mut v = orig.clone(); + v.sort_by_key(|x| x.to_string()); + assert!(v.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); + // Sort with many pre-sorted runs. let mut v = orig.clone(); v.sort(); -- cgit 1.4.1-3-g733a5 From 21fde0903b394ca4765b17321a736983c43886cb Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 1 Mar 2018 16:07:58 +0000 Subject: Update documentation --- src/liballoc/slice.rs | 21 +++++++++++---------- 1 file changed, 11 insertions(+), 10 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 63b22bd0f6d..32ade0b0178 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1314,7 +1314,7 @@ impl [T] { /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of /// two or more sorted sequences concatenated one after another. /// - /// The algorithm allocates temporary storage the size of `self`. + /// The algorithm allocates temporary storage in a `Vec<(K, usize)` the length of the slice. /// /// # Examples /// @@ -1326,8 +1326,8 @@ impl [T] { /// ``` #[stable(feature = "slice_sort_by_key", since = "1.7.0")] #[inline] - pub fn sort_by_key(&mut self, f: F) - where F: FnMut(&T) -> B, B: Ord + pub fn sort_by_key(&mut self, f: F) + where F: FnMut(&T) -> K, K: Ord { let mut indices: Vec<_> = self.iter().map(f).enumerate().map(|(i, k)| (k, i)).collect(); // The elements of `indices` are unique, as they are indexed, so any sort will be stable @@ -1418,8 +1418,8 @@ impl [T] { /// Sorts the slice with a key extraction function, but may not preserve the order of equal /// elements. /// - /// Note that, currently, the key function for `sort_unstable_by_key` is called multiple times - /// per element, unlike `sort_stable_by_key`. + /// Note that, currently, the key function for [`sort_unstable_by_key`] is called multiple times + /// per element, unlike [`sort_by_key`]. /// /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), /// and `O(m n log m n)` worst-case, where the key function is `O(m)`. @@ -1432,8 +1432,8 @@ impl [T] { /// randomization to avoid degenerate cases, but with a fixed seed to always provide /// deterministic behavior. /// - /// Due to its key calling strategy, `sort_unstable_by_key` is likely to be slower than - /// `sort_by_key` in cases where the key function is expensive. + /// Due to its key calling strategy, [`sort_unstable_by_key`] is likely to be slower than + /// [`sort_by_key`] in cases where the key function is expensive. /// /// # Examples /// @@ -1444,12 +1444,13 @@ impl [T] { /// assert!(v == [1, 2, -3, 4, -5]); /// ``` /// + /// [`sort_by_key`]: #method.sort_by_key + /// [`sort_unstable_by_key`]: #method.sort_unstable_by_key /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by_key(&mut self, f: F) - where F: FnMut(&T) -> B, - B: Ord + pub fn sort_unstable_by_key(&mut self, f: F) + where F: FnMut(&T) -> K, K: Ord { core_slice::SliceExt::sort_unstable_by_key(self, f); } -- cgit 1.4.1-3-g733a5 From 7dcfc07d2c441e6e18c34dfe844c7bdc1c0137fe Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 2 Mar 2018 15:51:20 +0000 Subject: Cull the quadratic --- src/liballoc/slice.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 32ade0b0178..3fa5e78c04f 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1339,6 +1339,7 @@ impl [T] { while index < i { index = indices[index].1; } + indices[i].1 = index; self.swap(i, index); } } -- cgit 1.4.1-3-g733a5 From bdcc6f939a10bc23a434b2ef301238650841dec9 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 2 Mar 2018 16:28:55 +0000 Subject: Index enumeration by minimally sized type --- src/liballoc/slice.rs | 37 +++++++++++++++++++++++++------------ 1 file changed, 25 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 3fa5e78c04f..fbd8d879308 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -102,6 +102,7 @@ use core::mem::size_of; use core::mem; use core::ptr; use core::slice as core_slice; +use core::{u8, u16, u32}; use borrow::{Borrow, BorrowMut, ToOwned}; use boxed::Box; @@ -1329,19 +1330,31 @@ impl [T] { pub fn sort_by_key(&mut self, f: F) where F: FnMut(&T) -> K, K: Ord { - let mut indices: Vec<_> = self.iter().map(f).enumerate().map(|(i, k)| (k, i)).collect(); - // The elements of `indices` are unique, as they are indexed, so any sort will be stable - // with respect to the original slice. We use `sort_unstable` here because it requires less - // memory allocation. - indices.sort_unstable(); - for i in 0..self.len() { - let mut index = indices[i].1; - while index < i { - index = indices[index].1; - } - indices[i].1 = index; - self.swap(i, index); + // Helper macro for indexing our vector by the smallest possible type, to reduce allocation. + macro_rules! sort_by_key { + ($t:ty, $slice:ident, $f:ident) => ({ + let mut indices: Vec<_> = + $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); + // The elements of `indices` are unique, as they are indexed, so any sort will be + // stable with respect to the original slice. We use `sort_unstable` here because it + // requires less memory allocation. + indices.sort_unstable(); + for i in 0..$slice.len() { + let mut index = indices[i].1; + while (index as usize) < i { + index = indices[index as usize].1; + } + indices[i].1 = index; + $slice.swap(i, index as usize); + } + }) } + + let len = self.len(); + if len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) } + if len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) } + if len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) } + sort_by_key!(usize, self, f) } /// Sorts the slice, but may not preserve the order of equal elements. -- cgit 1.4.1-3-g733a5 From f41a26f2040dfa752825a7d1fdfbd5a8ae3310cf Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 16 Mar 2018 14:37:17 +0000 Subject: Add sort_by_cached_key method --- src/liballoc/slice.rs | 61 +++++++++++++++++++++++++++++++++++++-------- src/liballoc/tests/slice.rs | 9 ++++--- 2 files changed, 56 insertions(+), 14 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index fbd8d879308..306c467f048 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1301,6 +1301,45 @@ impl [T] { merge_sort(self, |a, b| compare(a, b) == Less); } + /// Sorts the slice with a key extraction function. + /// + /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log m n)` + /// worst-case, where the key function is `O(m)`. + /// + /// For expensive key functions (e.g. functions that are not simple property accesses or + /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be + /// significantly faster, as it does not recompute element keys. + /// + /// When applicable, unstable sorting is preferred because it is generally faster than stable + /// sorting and it doesn't allocate auxiliary memory. + /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). + /// + /// # Current implementation + /// + /// The current algorithm is an adaptive, iterative merge sort inspired by + /// [timsort](https://en.wikipedia.org/wiki/Timsort). + /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of + /// two or more sorted sequences concatenated one after another. + /// + /// Also, it allocates temporary storage half the size of `self`, but for short slices a + /// non-allocating insertion sort is used instead. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// v.sort_by_key(|k| k.abs()); + /// assert!(v == [1, 2, -3, 4, -5]); + /// ``` + #[stable(feature = "slice_sort_by_key", since = "1.7.0")] + #[inline] + pub fn sort_by_key(&mut self, mut f: F) + where F: FnMut(&T) -> K, K: Ord + { + merge_sort(self, |a, b| f(a).lt(&f(b))); + } + /// Sorts the slice with a key extraction function. /// /// During sorting, the key function is called only once per element. @@ -1308,6 +1347,10 @@ impl [T] { /// This sort is stable (i.e. does not reorder equal elements) and `O(m n + n log n)` /// worst-case, where the key function is `O(m)`. /// + /// For simple key functions (e.g. functions that are property accesses or + /// basic operations), [`sort_by_key`](#method.sort_by_key) is likely to be + /// faster. + /// /// # Current implementation /// /// The current algorithm is an adaptive, iterative merge sort inspired by @@ -1315,19 +1358,19 @@ impl [T] { /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of /// two or more sorted sequences concatenated one after another. /// - /// The algorithm allocates temporary storage in a `Vec<(K, usize)` the length of the slice. + /// The algorithm allocates temporary storage in a `Vec<(K, usize)>` the length of the slice. /// /// # Examples /// /// ``` /// let mut v = [-5i32, 4, 1, -3, 2]; /// - /// v.sort_by_key(|k| k.abs()); + /// v.sort_by_cached_key(|k| k.abs()); /// assert!(v == [1, 2, -3, 4, -5]); /// ``` - #[stable(feature = "slice_sort_by_key", since = "1.7.0")] + #[unstable(feature = "slice_sort_by_uncached_key", issue = "34447")] #[inline] - pub fn sort_by_key(&mut self, f: F) + pub fn sort_by_cached_key(&mut self, f: F) where F: FnMut(&T) -> K, K: Ord { // Helper macro for indexing our vector by the smallest possible type, to reduce allocation. @@ -1432,9 +1475,6 @@ impl [T] { /// Sorts the slice with a key extraction function, but may not preserve the order of equal /// elements. /// - /// Note that, currently, the key function for [`sort_unstable_by_key`] is called multiple times - /// per element, unlike [`sort_by_key`]. - /// /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), /// and `O(m n log m n)` worst-case, where the key function is `O(m)`. /// @@ -1446,8 +1486,9 @@ impl [T] { /// randomization to avoid degenerate cases, but with a fixed seed to always provide /// deterministic behavior. /// - /// Due to its key calling strategy, [`sort_unstable_by_key`] is likely to be slower than - /// [`sort_by_key`] in cases where the key function is expensive. + /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) + /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_uncached_key) in + /// cases where the key function is expensive. /// /// # Examples /// @@ -1458,8 +1499,6 @@ impl [T] { /// assert!(v == [1, 2, -3, 4, -5]); /// ``` /// - /// [`sort_by_key`]: #method.sort_by_key - /// [`sort_unstable_by_key`]: #method.sort_unstable_by_key /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 300f6abaa7f..7d4dac1c5ec 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -426,9 +426,12 @@ fn test_sort() { assert!(v.windows(2).all(|w| w[0] >= w[1])); // Sort in lexicographic order. - let mut v = orig.clone(); - v.sort_by_key(|x| x.to_string()); - assert!(v.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); + let mut v1 = orig.clone(); + let mut v2 = orig.clone(); + v1.sort_by_key(|x| x.to_string()); + v2.sort_by_cached_key(|x| x.to_string()); + assert!(v1.windows(2).all(|w| w[0].to_string() <= w[1].to_string())); + assert!(v1 == v2); // Sort with many pre-sorted runs. let mut v = orig.clone(); -- cgit 1.4.1-3-g733a5 From b430cba343743783cee517bf93c7f255827cccc5 Mon Sep 17 00:00:00 2001 From: varkor Date: Fri, 16 Mar 2018 16:35:27 +0000 Subject: Fix use of unstable feature in test --- src/liballoc/lib.rs | 1 + src/liballoc/slice.rs | 4 ++-- src/liballoc/tests/lib.rs | 1 + 3 files changed, 4 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index b93e128d508..253b8acc16c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -113,6 +113,7 @@ #![feature(slice_get_slice)] #![feature(slice_patterns)] #![feature(slice_rsplit)] +#![feature(slice_sort_by_cached_key)] #![feature(specialization)] #![feature(staged_api)] #![feature(str_internals)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 306c467f048..db890066447 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1368,7 +1368,7 @@ impl [T] { /// v.sort_by_cached_key(|k| k.abs()); /// assert!(v == [1, 2, -3, 4, -5]); /// ``` - #[unstable(feature = "slice_sort_by_uncached_key", issue = "34447")] + #[unstable(feature = "slice_sort_by_cached_key", issue = "34447")] #[inline] pub fn sort_by_cached_key(&mut self, f: F) where F: FnMut(&T) -> K, K: Ord @@ -1487,7 +1487,7 @@ impl [T] { /// deterministic behavior. /// /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) - /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_uncached_key) in + /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in /// cases where the key function is expensive. /// /// # Examples diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 285cba0270c..27f3028a513 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -23,6 +23,7 @@ #![feature(pattern)] #![feature(placement_in_syntax)] #![feature(rand)] +#![feature(slice_sort_by_cached_key)] #![feature(splice)] #![feature(str_escape)] #![feature(string_retain)] -- cgit 1.4.1-3-g733a5 From 3753e1a55a2ed118121779e05f77164da3269a86 Mon Sep 17 00:00:00 2001 From: Niv Kaminer Date: Sat, 17 Mar 2018 01:09:36 +0200 Subject: update FIXME(#5244) to point to RFC 1109 (Non-Copy array creation ergonomics) --- src/liballoc/tests/slice.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index d9e9d91cea8..3f679d81f08 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -1351,7 +1351,7 @@ fn test_copy_from_slice_dst_shorter() { const MAX_LEN: usize = 80; static DROP_COUNTS: [AtomicUsize; MAX_LEN] = [ - // FIXME #5244: AtomicUsize is not Copy. + // FIXME(RFC 1109): AtomicUsize is not Copy. AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), AtomicUsize::new(0), -- cgit 1.4.1-3-g733a5 From ca3bed0c66d27fbf30eb48ae3eb5af235669364d Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 17 Mar 2018 20:18:08 +0000 Subject: Improve and fix documentation for sort_by_cached_key --- src/liballoc/slice.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index db890066447..bef50a733cc 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1353,21 +1353,25 @@ impl [T] { /// /// # Current implementation /// - /// The current algorithm is an adaptive, iterative merge sort inspired by - /// [timsort](https://en.wikipedia.org/wiki/Timsort). - /// It is designed to be very fast in cases where the slice is nearly sorted, or consists of - /// two or more sorted sequences concatenated one after another. + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. /// - /// The algorithm allocates temporary storage in a `Vec<(K, usize)>` the length of the slice. + /// In the worst case, the algorithm allocates temporary storage in a `Vec<(K, usize)>` the + /// length of the slice. /// /// # Examples /// /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; + /// let mut v = [-5i32, 4, 32, -3, 2]; /// - /// v.sort_by_cached_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); + /// v.sort_by_cached_key(|k| k.to_string()); + /// assert!(v == [-3, -5, 2, 32, 4]); /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort #[unstable(feature = "slice_sort_by_cached_key", issue = "34447")] #[inline] pub fn sort_by_cached_key(&mut self, f: F) -- cgit 1.4.1-3-g733a5 From b57ea5615921609815752c2d2133956b8a4fded6 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 17 Mar 2018 21:41:14 +0000 Subject: Stabilise FromUtf8Error::as_bytes Closes #40895. --- src/liballoc/string.rs | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 9fec9091498..e253122ffd6 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1576,7 +1576,6 @@ impl FromUtf8Error { /// Basic usage: /// /// ``` - /// #![feature(from_utf8_error_as_bytes)] /// // some invalid bytes, in a vector /// let bytes = vec![0, 159]; /// @@ -1584,7 +1583,7 @@ impl FromUtf8Error { /// /// assert_eq!(&[0, 159], value.unwrap_err().as_bytes()); /// ``` - #[unstable(feature = "from_utf8_error_as_bytes", reason = "recently added", issue = "40895")] + #[stable(feature = "from_utf8_error_as_bytes", since = "1.26.0")] pub fn as_bytes(&self) -> &[u8] { &self.bytes[..] } -- cgit 1.4.1-3-g733a5 From 2d13ddb6e14322edcd07135a436d0d848d127fb2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 16 Feb 2018 10:58:48 +0100 Subject: Use NonNull<_> instead of NonZero<*const _> in btree internals --- src/liballoc/btree/node.rs | 30 ++++++++++++++---------------- 1 file changed, 14 insertions(+), 16 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index c1618043ce6..464f8f2f4ec 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -43,8 +43,7 @@ use core::marker::PhantomData; use core::mem; -use core::nonzero::NonZero; -use core::ptr::{self, Unique}; +use core::ptr::{self, Unique, NonNull}; use core::slice; use boxed::Box; @@ -149,14 +148,12 @@ impl BoxedNode { } } - unsafe fn from_ptr(ptr: NonZero<*const LeafNode>) -> Self { - BoxedNode { ptr: Unique::new_unchecked(ptr.get() as *mut LeafNode) } + unsafe fn from_ptr(ptr: NonNull>) -> Self { + BoxedNode { ptr: Unique::from(ptr) } } - fn as_ptr(&self) -> NonZero<*const LeafNode> { - unsafe { - NonZero::from(self.ptr.as_ref()) - } + fn as_ptr(&self) -> NonNull> { + NonNull::from(self.ptr) } } @@ -276,7 +273,7 @@ impl Root { /// `NodeRef` could be pointing to either type of node. pub struct NodeRef { height: usize, - node: NonZero<*const LeafNode>, + node: NonNull>, // This is null unless the borrow type is `Mut` root: *const Root, _marker: PhantomData<(BorrowType, Type)> @@ -302,7 +299,7 @@ unsafe impl Send impl NodeRef { fn as_internal(&self) -> &InternalNode { unsafe { - &*(self.node.get() as *const InternalNode) + &*(self.node.as_ptr() as *mut InternalNode) } } } @@ -310,7 +307,7 @@ impl NodeRef { impl<'a, K, V> NodeRef, K, V, marker::Internal> { fn as_internal_mut(&mut self) -> &mut InternalNode { unsafe { - &mut *(self.node.get() as *mut InternalNode) + &mut *(self.node.as_ptr() as *mut InternalNode) } } } @@ -352,7 +349,7 @@ impl NodeRef { fn as_leaf(&self) -> &LeafNode { unsafe { - &*self.node.get() + self.node.as_ref() } } @@ -382,7 +379,8 @@ impl NodeRef { >, Self > { - if let Some(non_zero) = NonZero::new(self.as_leaf().parent as *const LeafNode) { + let parent_as_leaf = self.as_leaf().parent as *const LeafNode; + if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { Ok(Handle { node: NodeRef { height: self.height + 1, @@ -498,7 +496,7 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { fn as_leaf_mut(&mut self) -> &mut LeafNode { unsafe { - &mut *(self.node.get() as *mut LeafNode) + self.node.as_mut() } } @@ -1241,12 +1239,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Heap.dealloc( - right_node.node.get() as *mut u8, + right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); } else { Heap.dealloc( - right_node.node.get() as *mut u8, + right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); } -- cgit 1.4.1-3-g733a5 From 9896b38f01c068abfe7170cb9ae2bfadb4aebbc4 Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 17 Mar 2018 20:43:46 +0000 Subject: Clarify time complexity --- src/liballoc/lib.rs | 1 - src/liballoc/slice.rs | 5 +++-- 2 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 253b8acc16c..b93e128d508 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -113,7 +113,6 @@ #![feature(slice_get_slice)] #![feature(slice_patterns)] #![feature(slice_rsplit)] -#![feature(slice_sort_by_cached_key)] #![feature(specialization)] #![feature(staged_api)] #![feature(str_internals)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index bef50a733cc..cd212aa15ba 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1303,7 +1303,7 @@ impl [T] { /// Sorts the slice with a key extraction function. /// - /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log m n)` + /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log(m n))` /// worst-case, where the key function is `O(m)`. /// /// For expensive key functions (e.g. functions that are not simple property accesses or @@ -1365,6 +1365,7 @@ impl [T] { /// # Examples /// /// ``` + /// #![feature(slice_sort_by_cached_key)] /// let mut v = [-5i32, 4, 32, -3, 2]; /// /// v.sort_by_cached_key(|k| k.to_string()); @@ -1480,7 +1481,7 @@ impl [T] { /// elements. /// /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), - /// and `O(m n log m n)` worst-case, where the key function is `O(m)`. + /// and `O(m n log(m n))` worst-case, where the key function is `O(m)`. /// /// # Current implementation /// -- cgit 1.4.1-3-g733a5 From 81edd1796b463776d111cd4fe48e866dd716dfab Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 18 Mar 2018 11:11:17 +0000 Subject: Check that the size optimisation is not redundant --- src/liballoc/slice.rs | 15 ++++++++++----- 1 file changed, 10 insertions(+), 5 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index cd212aa15ba..2b4ce9fe49c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1384,8 +1384,8 @@ impl [T] { let mut indices: Vec<_> = $slice.iter().map($f).enumerate().map(|(i, k)| (k, i as $t)).collect(); // The elements of `indices` are unique, as they are indexed, so any sort will be - // stable with respect to the original slice. We use `sort_unstable` here because it - // requires less memory allocation. + // stable with respect to the original slice. We use `sort_unstable` here because + // it requires less memory allocation. indices.sort_unstable(); for i in 0..$slice.len() { let mut index = indices[i].1; @@ -1398,10 +1398,15 @@ impl [T] { }) } + let sz_u8 = mem::size_of::<(K, u8)>(); + let sz_u16 = mem::size_of::<(K, u16)>(); + let sz_u32 = mem::size_of::<(K, u32)>(); + let sz_usize = mem::size_of::<(K, usize)>(); + let len = self.len(); - if len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) } - if len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) } - if len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) } + if sz_u8 < sz_u16 && len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) } + if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) } + if sz_u32 < sz_usize && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) } sort_by_key!(usize, self, f) } -- cgit 1.4.1-3-g733a5 From 785e3c38fe6c49e39aec145c81e463ceb60d179e Mon Sep 17 00:00:00 2001 From: varkor Date: Sun, 18 Mar 2018 12:37:06 +0000 Subject: Add lexicographic sorting benchmark --- src/liballoc/benches/lib.rs | 1 + src/liballoc/benches/slice.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 2de0ffb4b26..9cb5f558574 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -13,6 +13,7 @@ #![feature(i128_type)] #![feature(rand)] #![feature(repr_simd)] +#![feature(slice_sort_by_cached_key)] #![feature(test)] extern crate rand; diff --git a/src/liballoc/benches/slice.rs b/src/liballoc/benches/slice.rs index ee5182a1d46..a699ff9c0a7 100644 --- a/src/liballoc/benches/slice.rs +++ b/src/liballoc/benches/slice.rs @@ -284,6 +284,17 @@ macro_rules! sort_expensive { } } +macro_rules! sort_lexicographic { + ($f:ident, $name:ident, $gen:expr, $len:expr) => { + #[bench] + fn $name(b: &mut Bencher) { + let v = $gen($len); + b.iter(|| v.clone().$f(|x| x.to_string())); + b.bytes = $len * mem::size_of_val(&$gen(1)[0]) as u64; + } + } +} + sort!(sort, sort_small_ascending, gen_ascending, 10); sort!(sort, sort_small_descending, gen_descending, 10); sort!(sort, sort_small_random, gen_random, 10); @@ -312,6 +323,10 @@ sort!(sort_unstable, sort_unstable_large_big, gen_big_random, 10000); sort_strings!(sort_unstable, sort_unstable_large_strings, gen_strings, 10000); sort_expensive!(sort_unstable_by, sort_unstable_large_expensive, gen_random, 10000); +sort_lexicographic!(sort_by_key, sort_by_key_lexicographic, gen_random, 10000); +sort_lexicographic!(sort_unstable_by_key, sort_unstable_by_key_lexicographic, gen_random, 10000); +sort_lexicographic!(sort_by_cached_key, sort_by_cached_key_lexicographic, gen_random, 10000); + macro_rules! reverse { ($name:ident, $ty:ty, $f:expr) => { #[bench] -- cgit 1.4.1-3-g733a5 From 2797aaca77fe5c454f3a3ada84b06912b2f74b9f Mon Sep 17 00:00:00 2001 From: boats Date: Sun, 18 Mar 2018 15:05:45 -0700 Subject: Update tracking issue. --- src/liballoc/boxed.rs | 24 ++++++++++++------------ src/libcore/marker.rs | 2 +- src/libcore/mem.rs | 28 ++++++++++++++-------------- 3 files changed, 27 insertions(+), 27 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 46d3ccb9de5..0e71cc59d94 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -898,22 +898,22 @@ impl Generator for Box } /// A pinned, heap allocated reference. -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] #[fundamental] pub struct PinBox { inner: Box, } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl PinBox { /// Allocate memory on the heap, move the data into it and pin it. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub fn new(data: T) -> PinBox { PinBox { inner: Box::new(data) } } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl PinBox { /// Get a pinned reference to the data in this PinBox. pub fn as_pin<'a>(&'a mut self) -> Pin<'a, T> { @@ -937,21 +937,21 @@ impl PinBox { } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl From> for PinBox { fn from(boxed: Box) -> PinBox { PinBox { inner: boxed } } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl From> for Box { fn from(pinned: PinBox) -> Box { pinned.inner } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl Deref for PinBox { type Target = T; @@ -960,28 +960,28 @@ impl Deref for PinBox { } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl DerefMut for PinBox { fn deref_mut(&mut self) -> &mut T { &mut *self.inner } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl fmt::Display for PinBox { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&*self.inner, f) } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl fmt::Debug for PinBox { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&*self.inner, f) } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl fmt::Pointer for PinBox { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { // It's not possible to extract the inner Uniq directly from the Box, @@ -991,5 +991,5 @@ impl fmt::Pointer for PinBox { } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl, U: ?Sized> CoerceUnsized> for PinBox {} diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 7b67404db5d..9d7f8abff15 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -573,5 +573,5 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// `Pin` pointer. /// /// This trait is automatically implemented for almost every type. -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] pub unsafe auto trait Unpin {} diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index e960b5ae758..b2467c948b4 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1111,24 +1111,24 @@ pub unsafe fn unreachable() -> ! { /// A pinned reference is a lot like a mutable reference, except that it is not /// safe to move a value out of a pinned reference unless the type of that /// value implements the `Unpin` trait. -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] #[fundamental] pub struct Pin<'a, T: ?Sized + 'a> { inner: &'a mut T, } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized + Unpin> Pin<'a, T> { /// Construct a new `Pin` around a reference to some data of a type that /// implements `Unpin`. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub fn new(reference: &'a mut T) -> Pin<'a, T> { Pin { inner: reference } } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized> Pin<'a, T> { /// Construct a new `Pin` around a reference to some data of a type that /// may or may not implement `Unpin`. @@ -1136,13 +1136,13 @@ impl<'a, T: ?Sized> Pin<'a, T> { /// This constructor is unsafe because we do not know what will happen with /// that data after the reference ends. If you cannot guarantee that the /// data will never move again, calling this constructor is invalid. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub unsafe fn new_unchecked(reference: &'a mut T) -> Pin<'a, T> { Pin { inner: reference } } /// Borrow a Pin for a shorter lifetime than it already has. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub fn borrow<'b>(this: &'b mut Pin<'a, T>) -> Pin<'b, T> { Pin { inner: this.inner } } @@ -1152,7 +1152,7 @@ impl<'a, T: ?Sized> Pin<'a, T> { /// This function is unsafe. You must guarantee that you will never move /// the data out of the mutable reference you receive when you call this /// function. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub unsafe fn get_mut<'b>(this: &'b mut Pin<'a, T>) -> &'b mut T { this.inner } @@ -1166,7 +1166,7 @@ impl<'a, T: ?Sized> Pin<'a, T> { /// will not move so long as the argument value does not move (for example, /// because it is one of the fields of that value), and also that you do /// not move out of the argument you receive to the interior function. - #[unstable(feature = "pin", issue = "0")] + #[unstable(feature = "pin", issue = "49150")] pub unsafe fn map<'b, U, F>(this: &'b mut Pin<'a, T>, f: F) -> Pin<'b, U> where F: FnOnce(&mut T) -> &mut U { @@ -1174,7 +1174,7 @@ impl<'a, T: ?Sized> Pin<'a, T> { } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized> Deref for Pin<'a, T> { type Target = T; @@ -1183,33 +1183,33 @@ impl<'a, T: ?Sized> Deref for Pin<'a, T> { } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized + Unpin> DerefMut for Pin<'a, T> { fn deref_mut(&mut self) -> &mut T { self.inner } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for Pin<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: fmt::Display + ?Sized> fmt::Display for Pin<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&**self, f) } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized> fmt::Pointer for Pin<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&(&*self.inner as *const T), f) } } -#[unstable(feature = "pin", issue = "0")] +#[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for Pin<'a, T> {} -- cgit 1.4.1-3-g733a5 From eca1e18cd7021f01757640c0c5ef63717870686c Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 19 Mar 2018 00:11:47 +0000 Subject: Add stability test for sort_by_cached_key --- src/liballoc/tests/slice.rs | 11 ++++++++--- 1 file changed, 8 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 7d4dac1c5ec..66c7dd75c87 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -485,7 +485,7 @@ fn test_sort_stability() { // the second item represents which occurrence of that // number this element is, i.e. the second elements // will occur in sorted order. - let mut v: Vec<_> = (0..len) + let mut orig: Vec<_> = (0..len) .map(|_| { let n = thread_rng().gen::() % 10; counts[n] += 1; @@ -493,16 +493,21 @@ fn test_sort_stability() { }) .collect(); - // only sort on the first element, so an unstable sort + let mut v = orig.clone(); + // Only sort on the first element, so an unstable sort // may mix up the counts. v.sort_by(|&(a, _), &(b, _)| a.cmp(&b)); - // this comparison includes the count (the second item + // This comparison includes the count (the second item // of the tuple), so elements with equal first items // will need to be ordered with increasing // counts... i.e. exactly asserting that this sort is // stable. assert!(v.windows(2).all(|w| w[0] <= w[1])); + + let mut v = orig.clone(); + v.sort_by_cached_key(|&(x, _)| x); + assert!(v.windows(2).all(|w| w[0] <= w[1])); } } } -- cgit 1.4.1-3-g733a5 From 741d7a5598739f864f0f842d21665fa1e5809b41 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 19 Mar 2018 07:37:59 +0100 Subject: Docs: fix incorrect copy-paste for new `X?` in formatting strings --- src/liballoc/fmt.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index 2c4cdef03b0..90043e1c716 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -114,7 +114,7 @@ //! * *nothing* ⇒ [`Display`] //! * `?` ⇒ [`Debug`] //! * `x?` ⇒ [`Debug`] with lower-case hexadecimal integers -//! * `X?` ⇒ [`Debug`] with lower-case hexadecimal integers +//! * `X?` ⇒ [`Debug`] with upper-case hexadecimal integers //! * `o` ⇒ [`Octal`](trait.Octal.html) //! * `x` ⇒ [`LowerHex`](trait.LowerHex.html) //! * `X` ⇒ [`UpperHex`](trait.UpperHex.html) -- cgit 1.4.1-3-g733a5 From 7c90189e1331cea3eac0ab0e8959f664cffba1ae Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Sat, 24 Feb 2018 22:21:33 +0300 Subject: Stabilize slice patterns without `..` Merge `feature(advanced_slice_patterns)` into `feature(slice_patterns)` --- .../language-features/advanced-slice-patterns.md | 35 --------------- .../src/language-features/slice-patterns.md | 28 ++++++------ src/liballoc/lib.rs | 1 - src/libcore/benches/lib.rs | 1 - src/libcore/tests/lib.rs | 1 + src/librustc/benches/lib.rs | 1 - src/librustc_apfloat/lib.rs | 2 +- src/librustc_const_eval/lib.rs | 1 - src/librustc_lint/lib.rs | 1 - src/librustc_trans/lib.rs | 2 +- src/librustc_trans_utils/lib.rs | 1 - src/librustc_typeck/diagnostics.rs | 6 --- src/librustc_typeck/lib.rs | 2 +- src/librustdoc/lib.rs | 2 +- src/libsyntax/feature_gate.rs | 16 +++---- src/libsyntax/parse/parser.rs | 2 +- .../borrowck/borrowck-describe-lvalue.rs | 1 - .../borrowck-match-binding-is-assignment.rs | 2 - .../borrowck/borrowck-move-out-from-array.rs | 3 +- .../borrowck/borrowck-vec-pattern-element-loan.rs | 1 - src/test/compile-fail/issue-12369.rs | 1 - src/test/compile-fail/issue-13482-2.rs | 2 - src/test/compile-fail/issue-13482.rs | 2 - src/test/compile-fail/issue-15381.rs | 2 - src/test/compile-fail/issue-41255.rs | 1 - src/test/compile-fail/issue-6804.rs | 1 - .../compile-fail/match-byte-array-patterns-2.rs | 2 - src/test/compile-fail/match-byte-array-patterns.rs | 2 +- src/test/compile-fail/match-ref-ice.rs | 1 - src/test/compile-fail/match-slice-patterns.rs | 2 +- src/test/compile-fail/match-vec-fixed.rs | 1 - src/test/compile-fail/match-vec-mismatch-2.rs | 2 - src/test/compile-fail/match-vec-unreachable.rs | 1 - src/test/compile-fail/move-out-of-slice-1.rs | 2 +- .../regions-pattern-typing-issue-19552.rs | 2 - .../uninhabited-matches-feature-gated.rs | 2 - src/test/compile-fail/uninhabited-patterns.rs | 2 +- src/test/mir-opt/uniform_array_move_out.rs | 3 +- .../run-pass-fulldeps/auxiliary/roman_numerals.rs | 1 - src/test/run-pass/destructure-array-1.rs | 3 -- src/test/run-pass/dynamic-drop.rs | 3 +- src/test/run-pass/ignore-all-the-things.rs | 11 +++-- src/test/run-pass/issue-13027.rs | 2 - src/test/run-pass/issue-15080.rs | 1 - src/test/run-pass/issue-15104.rs | 1 - src/test/run-pass/issue-16648.rs | 3 -- src/test/run-pass/issue-17877.rs | 1 - src/test/run-pass/issue-37598.rs | 2 +- src/test/run-pass/issue-38002.rs | 2 - src/test/run-pass/issue-46855.rs | 2 - src/test/run-pass/issue-7784.rs | 2 - src/test/run-pass/match-vec-alternatives.rs | 2 - .../rfc-2005-default-binding-mode/slice.rs | 2 +- src/test/run-pass/trailing-comma.rs | 1 - src/test/run-pass/vec-matching-autoslice.rs | 3 -- src/test/run-pass/vec-matching-fixed.rs | 2 - src/test/run-pass/vec-matching-fold.rs | 2 - src/test/run-pass/vec-matching.rs | 2 - src/test/run-pass/vec-tail-matching.rs | 2 - .../ui/borrowck/borrowck-vec-pattern-nesting.rs | 1 - .../borrowck/borrowck-vec-pattern-nesting.stderr | 16 +++---- src/test/ui/error-codes/E0527.rs | 2 - src/test/ui/error-codes/E0527.stderr | 2 +- src/test/ui/error-codes/E0529.rs | 2 - src/test/ui/error-codes/E0529.stderr | 2 +- .../ui/feature-gate-advanced-slice-features.rs | 22 ---------- .../ui/feature-gate-advanced-slice-features.stderr | 19 -------- src/test/ui/feature-gate-slice-patterns.rs | 13 +++++- src/test/ui/feature-gate-slice-patterns.stderr | 50 +++++++++++++++++++--- src/test/ui/mismatched_types/issue-38371.rs | 2 - src/test/ui/mismatched_types/issue-38371.stderr | 8 ++-- src/test/ui/non-exhaustive-pattern-witness.rs | 1 - src/test/ui/non-exhaustive-pattern-witness.stderr | 14 +++--- src/test/ui/pat-slice-old-style.rs | 4 +- src/test/ui/rfc-2005-default-binding-mode/slice.rs | 2 +- 75 files changed, 124 insertions(+), 226 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/advanced-slice-patterns.md delete mode 100644 src/test/ui/feature-gate-advanced-slice-features.rs delete mode 100644 src/test/ui/feature-gate-advanced-slice-features.stderr (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/advanced-slice-patterns.md b/src/doc/unstable-book/src/language-features/advanced-slice-patterns.md deleted file mode 100644 index e8256469b14..00000000000 --- a/src/doc/unstable-book/src/language-features/advanced-slice-patterns.md +++ /dev/null @@ -1,35 +0,0 @@ -# `advanced_slice_patterns` - -The tracking issue for this feature is: [#23121] - -[#23121]: https://github.com/rust-lang/rust/issues/23121 - -See also [`slice_patterns`](language-features/slice-patterns.html). - ------------------------- - - -The `advanced_slice_patterns` gate lets you use `..` to indicate any number of -elements inside a pattern matching a slice. This wildcard can only be used once -for a given array. If there's an identifier before the `..`, the result of the -slice will be bound to that name. For example: - -```rust -#![feature(advanced_slice_patterns, slice_patterns)] - -fn is_symmetric(list: &[u32]) -> bool { - match list { - &[] | &[_] => true, - &[x, ref inside.., y] if x == y => is_symmetric(inside), - _ => false - } -} - -fn main() { - let sym = &[0, 1, 4, 2, 4, 1, 0]; - assert!(is_symmetric(sym)); - - let not_sym = &[0, 1, 7, 2, 4, 1, 0]; - assert!(!is_symmetric(not_sym)); -} -``` diff --git a/src/doc/unstable-book/src/language-features/slice-patterns.md b/src/doc/unstable-book/src/language-features/slice-patterns.md index 69857297582..133174268ef 100644 --- a/src/doc/unstable-book/src/language-features/slice-patterns.md +++ b/src/doc/unstable-book/src/language-features/slice-patterns.md @@ -4,25 +4,29 @@ The tracking issue for this feature is: [#23121] [#23121]: https://github.com/rust-lang/rust/issues/23121 -See also -[`advanced_slice_patterns`](language-features/advanced-slice-patterns.html). - ------------------------ - -If you want to match against a slice or array, you can use `&` with the -`slice_patterns` feature: +The `slice_patterns` feature gate lets you use `..` to indicate any number of +elements inside a pattern matching a slice. This wildcard can only be used once +for a given array. If there's an pattern before the `..`, the subslice will be +matched against that pattern. For example: ```rust #![feature(slice_patterns)] +fn is_symmetric(list: &[u32]) -> bool { + match list { + &[] | &[_] => true, + &[x, ref inside.., y] if x == y => is_symmetric(inside), + &[..] => false, + } +} + fn main() { - let v = vec!["match_this", "1"]; + let sym = &[0, 1, 4, 2, 4, 1, 0]; + assert!(is_symmetric(sym)); - match &v[..] { - &["match_this", second] => println!("The second element is {}", second), - _ => {}, - } + let not_sym = &[0, 1, 7, 2, 4, 1, 0]; + assert!(!is_symmetric(not_sym)); } ``` - diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 2727bcaa28a..45bb3885574 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -110,7 +110,6 @@ #![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] -#![feature(slice_patterns)] #![feature(slice_rsplit)] #![feature(specialization)] #![feature(staged_api)] diff --git a/src/libcore/benches/lib.rs b/src/libcore/benches/lib.rs index 201064e823b..c947b003ccb 100644 --- a/src/libcore/benches/lib.rs +++ b/src/libcore/benches/lib.rs @@ -11,7 +11,6 @@ #![deny(warnings)] #![feature(flt2dec)] -#![feature(slice_patterns)] #![feature(test)] extern crate core; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 1c876fa0bd7..e9a8113ef10 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -37,6 +37,7 @@ #![feature(raw)] #![feature(refcell_replace_swap)] #![feature(slice_patterns)] +#![feature(slice_rotate)] #![feature(sort_internals)] #![feature(specialization)] #![feature(step_trait)] diff --git a/src/librustc/benches/lib.rs b/src/librustc/benches/lib.rs index 24294ec49ce..278e0f9a26e 100644 --- a/src/librustc/benches/lib.rs +++ b/src/librustc/benches/lib.rs @@ -10,7 +10,6 @@ #![deny(warnings)] -#![feature(slice_patterns)] #![feature(test)] extern crate test; diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 3afc2f68400..565658804b0 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -47,7 +47,7 @@ #![forbid(unsafe_code)] #![feature(i128_type)] -#![feature(slice_patterns)] +#![cfg_attr(stage0, feature(slice_patterns))] #![feature(try_from)] // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 459aa9ea488..2b0775e8695 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -20,7 +20,6 @@ #![deny(warnings)] #![feature(rustc_diagnostic_macros)] -#![feature(slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(macro_lifetime_matcher)] diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 8b86c905489..ce896bfb701 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -31,7 +31,6 @@ #![feature(macro_vis_matcher)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![feature(slice_patterns)] #![cfg_attr(stage0, feature(never_type))] #[macro_use] diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 3645e728842..337f85a3813 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -30,7 +30,7 @@ #![feature(libc)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![feature(slice_patterns)] +#![cfg_attr(stage0, feature(slice_patterns))] #![feature(conservative_impl_trait)] #![feature(optin_builtin_traits)] #![feature(inclusive_range_fields)] diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 6a3fd21f3a7..0af5f467934 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -24,7 +24,6 @@ #![feature(i128_type)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![feature(slice_patterns)] #![feature(conservative_impl_trait)] extern crate ar; diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 24044fd2d72..f5337118e30 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3559,8 +3559,6 @@ elements in the array being matched. Example of erroneous code: ```compile_fail,E0527 -#![feature(slice_patterns)] - let r = &[1, 2, 3, 4]; match r { &[a, b] => { // error: pattern requires 2 elements but array @@ -3625,8 +3623,6 @@ An array or slice pattern was matched against some other type. Example of erroneous code: ```compile_fail,E0529 -#![feature(slice_patterns)] - let r: f32 = 1.0; match r { [a, b] => { // error: expected an array or slice, found `f32` @@ -3639,8 +3635,6 @@ Ensure that the pattern and the expression being matched on are of consistent types: ``` -#![feature(slice_patterns)] - let r = [1.0, 2.0]; match r { [a, b] => { // ok! diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 964c0021133..9f98932f24b 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -72,7 +72,7 @@ This API is completely unstable and subject to change. #![allow(non_camel_case_types)] -#![feature(advanced_slice_patterns)] +#![cfg_attr(stage0, feature(advanced_slice_patterns))] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(conservative_impl_trait)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index da52fd5aa37..bec25a98227 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -20,7 +20,7 @@ #![feature(box_syntax)] #![feature(fs_read_write)] #![feature(set_stdio)] -#![feature(slice_patterns)] +#![cfg_attr(stage0, feature(slice_patterns))] #![feature(test)] #![feature(unicode)] #![feature(vec_remove_item)] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index fa600cd6860..915396d29fe 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -145,7 +145,6 @@ declare_features! ( // rustc internal (active, rustc_diagnostic_macros, "1.0.0", None, None), (active, rustc_const_unstable, "1.0.0", None, None), - (active, advanced_slice_patterns, "1.0.0", Some(23121), None), (active, box_syntax, "1.0.0", Some(27779), None), (active, placement_in_syntax, "1.0.0", Some(27779), None), (active, unboxed_closures, "1.0.0", Some(29625), None), @@ -474,6 +473,8 @@ declare_features! ( (removed, allocator, "1.0.0", None, None), // Allows the `#[simd]` attribute -- removed in favor of `#[repr(simd)]` (removed, simd, "1.0.0", Some(27731), None), + // Merged into `slice_patterns` + (removed, advanced_slice_patterns, "1.0.0", Some(23121), None), ); declare_features! ( @@ -1655,17 +1656,10 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { fn visit_pat(&mut self, pattern: &'a ast::Pat) { match pattern.node { - PatKind::Slice(_, Some(_), ref last) if !last.is_empty() => { - gate_feature_post!(&self, advanced_slice_patterns, - pattern.span, - "multiple-element slice matches anywhere \ - but at the end of a slice (e.g. \ - `[0, ..xs, 0]`) are experimental") - } - PatKind::Slice(..) => { + PatKind::Slice(_, Some(ref subslice), _) => { gate_feature_post!(&self, slice_patterns, - pattern.span, - "slice pattern syntax is experimental"); + subslice.span, + "syntax for subslices in slice patterns is not yet stabilized"); } PatKind::Box(..) => { gate_feature_post!(&self, box_patterns, diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index cb5010a638d..6d8975197d5 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -3618,7 +3618,7 @@ impl<'a> Parser<'a> { slice = Some(P(Pat { id: ast::DUMMY_NODE_ID, node: PatKind::Wild, - span: self.span, + span: self.prev_span, })); before_slice = false; } diff --git a/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs b/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs index 1d08b807465..fa475949b36 100644 --- a/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs +++ b/src/test/compile-fail/borrowck/borrowck-describe-lvalue.rs @@ -13,7 +13,6 @@ //[mir]compile-flags: -Z borrowck=mir #![feature(slice_patterns)] -#![feature(advanced_slice_patterns)] pub struct Foo { x: u32 diff --git a/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs b/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs index 32a573911ec..30047f84041 100644 --- a/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs +++ b/src/test/compile-fail/borrowck/borrowck-match-binding-is-assignment.rs @@ -13,8 +13,6 @@ // Test that immutable pattern bindings cannot be reassigned. -#![feature(slice_patterns)] - enum E { Foo(isize) } diff --git a/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs b/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs index e0116173462..0db31cef0ed 100644 --- a/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs +++ b/src/test/compile-fail/borrowck/borrowck-move-out-from-array.rs @@ -11,7 +11,8 @@ // revisions: ast mir //[mir]compile-flags: -Z borrowck=mir -#![feature(box_syntax, slice_patterns, advanced_slice_patterns)] +#![feature(box_syntax)] +#![feature(slice_patterns)] fn move_out_from_begin_and_end() { let a = [box 1, box 2]; diff --git a/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs b/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs index eb5d69d49bd..0fd6923326a 100644 --- a/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs +++ b/src/test/compile-fail/borrowck/borrowck-vec-pattern-element-loan.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] fn a<'a>() -> &'a [isize] { diff --git a/src/test/compile-fail/issue-12369.rs b/src/test/compile-fail/issue-12369.rs index 4df1e24dcfb..1b9af393ccc 100644 --- a/src/test/compile-fail/issue-12369.rs +++ b/src/test/compile-fail/issue-12369.rs @@ -9,7 +9,6 @@ // except according to those terms. #![feature(slice_patterns)] -#![allow(unused_variables)] #![deny(unreachable_patterns)] fn main() { diff --git a/src/test/compile-fail/issue-13482-2.rs b/src/test/compile-fail/issue-13482-2.rs index 6885c8d94c6..fe7fbb176cc 100644 --- a/src/test/compile-fail/issue-13482-2.rs +++ b/src/test/compile-fail/issue-13482-2.rs @@ -10,8 +10,6 @@ // compile-flags:-Z verbose -#![feature(slice_patterns)] - fn main() { let x = [1,2]; let y = match x { diff --git a/src/test/compile-fail/issue-13482.rs b/src/test/compile-fail/issue-13482.rs index 82e82df3186..32a63b79a32 100644 --- a/src/test/compile-fail/issue-13482.rs +++ b/src/test/compile-fail/issue-13482.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn main() { let x = [1,2]; let y = match x { diff --git a/src/test/compile-fail/issue-15381.rs b/src/test/compile-fail/issue-15381.rs index d0964d2aabe..1cdd803971b 100644 --- a/src/test/compile-fail/issue-15381.rs +++ b/src/test/compile-fail/issue-15381.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn main() { let values: Vec = vec![1,2,3,4,5,6,7,8]; diff --git a/src/test/compile-fail/issue-41255.rs b/src/test/compile-fail/issue-41255.rs index ac85e43cf4f..bdd502d4420 100644 --- a/src/test/compile-fail/issue-41255.rs +++ b/src/test/compile-fail/issue-41255.rs @@ -10,7 +10,6 @@ // Matching against float literals should result in a linter error -#![feature(slice_patterns)] #![feature(exclusive_range_pattern)] #![allow(unused)] #![forbid(illegal_floating_point_literal_pattern)] diff --git a/src/test/compile-fail/issue-6804.rs b/src/test/compile-fail/issue-6804.rs index 9191dfa155c..fffa27ab842 100644 --- a/src/test/compile-fail/issue-6804.rs +++ b/src/test/compile-fail/issue-6804.rs @@ -10,7 +10,6 @@ // Matching against NaN should result in a warning -#![feature(slice_patterns)] #![allow(unused)] #![deny(illegal_floating_point_literal_pattern)] diff --git a/src/test/compile-fail/match-byte-array-patterns-2.rs b/src/test/compile-fail/match-byte-array-patterns-2.rs index ad7e931a0ec..abb770df107 100644 --- a/src/test/compile-fail/match-byte-array-patterns-2.rs +++ b/src/test/compile-fail/match-byte-array-patterns-2.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns, slice_patterns)] - fn main() { let buf = &[0, 1, 2, 3]; diff --git a/src/test/compile-fail/match-byte-array-patterns.rs b/src/test/compile-fail/match-byte-array-patterns.rs index 1ff07eae1c9..9db4319b786 100644 --- a/src/test/compile-fail/match-byte-array-patterns.rs +++ b/src/test/compile-fail/match-byte-array-patterns.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns, slice_patterns)] +#![feature(slice_patterns)] #![deny(unreachable_patterns)] fn main() { diff --git a/src/test/compile-fail/match-ref-ice.rs b/src/test/compile-fail/match-ref-ice.rs index 1cdbba17f65..cb8f8fad532 100644 --- a/src/test/compile-fail/match-ref-ice.rs +++ b/src/test/compile-fail/match-ref-ice.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] #![deny(unreachable_patterns)] // The arity of `ref x` is always 1. If the pattern is compared to some non-structural type whose diff --git a/src/test/compile-fail/match-slice-patterns.rs b/src/test/compile-fail/match-slice-patterns.rs index fd4bd1c7b94..a8ec95da3d8 100644 --- a/src/test/compile-fail/match-slice-patterns.rs +++ b/src/test/compile-fail/match-slice-patterns.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns, slice_patterns)] +#![feature(slice_patterns)] fn check(list: &[Option<()>]) { match list { diff --git a/src/test/compile-fail/match-vec-fixed.rs b/src/test/compile-fail/match-vec-fixed.rs index dd9379c756d..05971d70167 100644 --- a/src/test/compile-fail/match-vec-fixed.rs +++ b/src/test/compile-fail/match-vec-fixed.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] #![deny(unreachable_patterns)] fn a() { diff --git a/src/test/compile-fail/match-vec-mismatch-2.rs b/src/test/compile-fail/match-vec-mismatch-2.rs index 375d855d1fd..52c5375f4e7 100644 --- a/src/test/compile-fail/match-vec-mismatch-2.rs +++ b/src/test/compile-fail/match-vec-mismatch-2.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn main() { match () { [()] => { } diff --git a/src/test/compile-fail/match-vec-unreachable.rs b/src/test/compile-fail/match-vec-unreachable.rs index 472b054b087..d6e3fdbe088 100644 --- a/src/test/compile-fail/match-vec-unreachable.rs +++ b/src/test/compile-fail/match-vec-unreachable.rs @@ -10,7 +10,6 @@ #![feature(slice_patterns)] #![deny(unreachable_patterns)] -#![allow(unused_variables)] fn main() { let x: Vec<(isize, isize)> = Vec::new(); diff --git a/src/test/compile-fail/move-out-of-slice-1.rs b/src/test/compile-fail/move-out-of-slice-1.rs index 9ca9e0984e4..5efbef549dd 100644 --- a/src/test/compile-fail/move-out-of-slice-1.rs +++ b/src/test/compile-fail/move-out-of-slice-1.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns, box_patterns)] +#![feature(box_patterns)] struct A; diff --git a/src/test/compile-fail/regions-pattern-typing-issue-19552.rs b/src/test/compile-fail/regions-pattern-typing-issue-19552.rs index 8e83177090b..3401dd1becd 100644 --- a/src/test/compile-fail/regions-pattern-typing-issue-19552.rs +++ b/src/test/compile-fail/regions-pattern-typing-issue-19552.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn assert_static(_t: T) {} fn main() { diff --git a/src/test/compile-fail/uninhabited-matches-feature-gated.rs b/src/test/compile-fail/uninhabited-matches-feature-gated.rs index 0c3ea53a903..1d3f8ff12d8 100644 --- a/src/test/compile-fail/uninhabited-matches-feature-gated.rs +++ b/src/test/compile-fail/uninhabited-matches-feature-gated.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - enum Void {} fn main() { diff --git a/src/test/compile-fail/uninhabited-patterns.rs b/src/test/compile-fail/uninhabited-patterns.rs index 9f943f08232..e130df5c845 100644 --- a/src/test/compile-fail/uninhabited-patterns.rs +++ b/src/test/compile-fail/uninhabited-patterns.rs @@ -9,9 +9,9 @@ // except according to those terms. #![feature(box_patterns)] -#![feature(slice_patterns)] #![feature(box_syntax)] #![feature(exhaustive_patterns)] +#![feature(slice_patterns)] #![deny(unreachable_patterns)] mod foo { diff --git a/src/test/mir-opt/uniform_array_move_out.rs b/src/test/mir-opt/uniform_array_move_out.rs index 482b69a59dd..fa5f62f89f6 100644 --- a/src/test/mir-opt/uniform_array_move_out.rs +++ b/src/test/mir-opt/uniform_array_move_out.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(box_syntax, slice_patterns, advanced_slice_patterns)] +#![feature(box_syntax)] +#![feature(slice_patterns)] fn move_out_from_end() { let a = [box 1, box 2]; diff --git a/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs b/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs index 26419a51513..17cf39372c0 100644 --- a/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs +++ b/src/test/run-pass-fulldeps/auxiliary/roman_numerals.rs @@ -12,7 +12,6 @@ #![crate_type="dylib"] #![feature(plugin_registrar, rustc_private)] -#![feature(slice_patterns)] extern crate syntax; extern crate syntax_pos; diff --git a/src/test/run-pass/destructure-array-1.rs b/src/test/run-pass/destructure-array-1.rs index 0d24f0bd0d7..43271162c18 100644 --- a/src/test/run-pass/destructure-array-1.rs +++ b/src/test/run-pass/destructure-array-1.rs @@ -11,9 +11,6 @@ // Ensure that we can do a destructuring bind of a fixed-size array, // even when the element type has a destructor. - -#![feature(slice_patterns)] - struct D { x: u8 } impl Drop for D { fn drop(&mut self) { } } diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 4d0bd3f3412..1f543f7be0e 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -13,7 +13,8 @@ // ignore-wasm32-bare compiled with panic=abort by default -#![feature(generators, generator_trait, untagged_unions, slice_patterns, advanced_slice_patterns)] +#![feature(generators, generator_trait, untagged_unions)] +#![feature(slice_patterns)] use std::cell::{Cell, RefCell}; use std::ops::Generator; diff --git a/src/test/run-pass/ignore-all-the-things.rs b/src/test/run-pass/ignore-all-the-things.rs index 711f2dd6c66..c14f3dc7291 100644 --- a/src/test/run-pass/ignore-all-the-things.rs +++ b/src/test/run-pass/ignore-all-the-things.rs @@ -10,7 +10,6 @@ // pretty-expanded FIXME #23616 -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] struct Foo(isize, isize, isize, isize); @@ -20,11 +19,11 @@ pub fn main() { let Foo(..) = Foo(5, 5, 5, 5); let Foo(..) = Foo(5, 5, 5, 5); let Bar{..} = Bar{a: 5, b: 5, c: 5, d: 5}; - //let (..) = (5, 5, 5, 5); - //let Foo(a, b, ..) = Foo(5, 5, 5, 5); - //let Foo(.., d) = Foo(5, 5, 5, 5); - //let (a, b, ..) = (5, 5, 5, 5); - //let (.., c, d) = (5, 5, 5, 5); + let (..) = (5, 5, 5, 5); + let Foo(a, b, ..) = Foo(5, 5, 5, 5); + let Foo(.., d) = Foo(5, 5, 5, 5); + let (a, b, ..) = (5, 5, 5, 5); + let (.., c, d) = (5, 5, 5, 5); let Bar{b: b, ..} = Bar{a: 5, b: 5, c: 5, d: 5}; match [5, 5, 5, 5] { [..] => { } diff --git a/src/test/run-pass/issue-13027.rs b/src/test/run-pass/issue-13027.rs index 14987484711..d28ea94ec1a 100644 --- a/src/test/run-pass/issue-13027.rs +++ b/src/test/run-pass/issue-13027.rs @@ -12,8 +12,6 @@ // Tests that match expression handles overlapped literal and range // properly in the presence of guard function. -#![feature(slice_patterns)] - fn val() -> usize { 1 } static CONST: usize = 1; diff --git a/src/test/run-pass/issue-15080.rs b/src/test/run-pass/issue-15080.rs index 14e00378846..59267f79e26 100644 --- a/src/test/run-pass/issue-15080.rs +++ b/src/test/run-pass/issue-15080.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - #![feature(slice_patterns)] fn main() { diff --git a/src/test/run-pass/issue-15104.rs b/src/test/run-pass/issue-15104.rs index 508360cb701..2878f2795c5 100644 --- a/src/test/run-pass/issue-15104.rs +++ b/src/test/run-pass/issue-15104.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - #![feature(slice_patterns)] fn main() { diff --git a/src/test/run-pass/issue-16648.rs b/src/test/run-pass/issue-16648.rs index e1b94179764..bf272308fa9 100644 --- a/src/test/run-pass/issue-16648.rs +++ b/src/test/run-pass/issue-16648.rs @@ -8,9 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(slice_patterns)] - fn main() { let x: (isize, &[isize]) = (2, &[1, 2]); assert_eq!(match x { diff --git a/src/test/run-pass/issue-17877.rs b/src/test/run-pass/issue-17877.rs index 6c87e8d35fb..d3fe0903a1d 100644 --- a/src/test/run-pass/issue-17877.rs +++ b/src/test/run-pass/issue-17877.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - #![feature(slice_patterns)] fn main() { diff --git a/src/test/run-pass/issue-37598.rs b/src/test/run-pass/issue-37598.rs index d32d2fc2954..e97c8d9f417 100644 --- a/src/test/run-pass/issue-37598.rs +++ b/src/test/run-pass/issue-37598.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns, slice_patterns)] +#![feature(slice_patterns)] fn check(list: &[u8]) { match list { diff --git a/src/test/run-pass/issue-38002.rs b/src/test/run-pass/issue-38002.rs index 489d35e9147..dd6ccec973f 100644 --- a/src/test/run-pass/issue-38002.rs +++ b/src/test/run-pass/issue-38002.rs @@ -10,8 +10,6 @@ // Check that constant ADTs are translated OK, part k of N. -#![feature(slice_patterns)] - enum Bar { C } diff --git a/src/test/run-pass/issue-46855.rs b/src/test/run-pass/issue-46855.rs index d864d55c939..28aa6c731ec 100644 --- a/src/test/run-pass/issue-46855.rs +++ b/src/test/run-pass/issue-46855.rs @@ -10,8 +10,6 @@ // compile-flags: -Zmir-opt-level=1 -#![feature(slice_patterns)] - use std::mem; #[derive(Copy, Clone)] diff --git a/src/test/run-pass/issue-7784.rs b/src/test/run-pass/issue-7784.rs index badc013cd62..8d21594aa12 100644 --- a/src/test/run-pass/issue-7784.rs +++ b/src/test/run-pass/issue-7784.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] use std::ops::Add; diff --git a/src/test/run-pass/match-vec-alternatives.rs b/src/test/run-pass/match-vec-alternatives.rs index fa609593c24..7d03d9c2abe 100644 --- a/src/test/run-pass/match-vec-alternatives.rs +++ b/src/test/run-pass/match-vec-alternatives.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] fn match_vecs<'a, T>(l1: &'a [T], l2: &'a [T]) -> &'static str { diff --git a/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs index 1717d0d54c0..6178f613b4b 100644 --- a/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs +++ b/src/test/run-pass/rfc-2005-default-binding-mode/slice.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] #![feature(match_default_bindings)] +#![feature(slice_patterns)] fn slice_pat() { let sl: &[u8] = b"foo"; diff --git a/src/test/run-pass/trailing-comma.rs b/src/test/run-pass/trailing-comma.rs index b9eda084653..02bae5aa455 100644 --- a/src/test/run-pass/trailing-comma.rs +++ b/src/test/run-pass/trailing-comma.rs @@ -10,7 +10,6 @@ // pretty-expanded FIXME #23616 -#![feature(advanced_slice_patterns,)] #![feature(slice_patterns)] fn f(_: T,) {} diff --git a/src/test/run-pass/vec-matching-autoslice.rs b/src/test/run-pass/vec-matching-autoslice.rs index 9df777e7af0..7268536a51f 100644 --- a/src/test/run-pass/vec-matching-autoslice.rs +++ b/src/test/run-pass/vec-matching-autoslice.rs @@ -8,9 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(slice_patterns)] - pub fn main() { let x = [1, 2, 3]; match x { diff --git a/src/test/run-pass/vec-matching-fixed.rs b/src/test/run-pass/vec-matching-fixed.rs index 1ed6ddc4110..060d152488a 100644 --- a/src/test/run-pass/vec-matching-fixed.rs +++ b/src/test/run-pass/vec-matching-fixed.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] fn a() { diff --git a/src/test/run-pass/vec-matching-fold.rs b/src/test/run-pass/vec-matching-fold.rs index ac80a4211ad..1a30f875580 100644 --- a/src/test/run-pass/vec-matching-fold.rs +++ b/src/test/run-pass/vec-matching-fold.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] use std::fmt::Debug; diff --git a/src/test/run-pass/vec-matching.rs b/src/test/run-pass/vec-matching.rs index bd0731a555c..ace418f2160 100644 --- a/src/test/run-pass/vec-matching.rs +++ b/src/test/run-pass/vec-matching.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] fn a() { diff --git a/src/test/run-pass/vec-tail-matching.rs b/src/test/run-pass/vec-tail-matching.rs index d123eb36a7d..4f31405ead5 100644 --- a/src/test/run-pass/vec-tail-matching.rs +++ b/src/test/run-pass/vec-tail-matching.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. - - #![feature(slice_patterns)] struct Foo { diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs index 07b268f1a4b..111968e9c93 100644 --- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs +++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns)] #![feature(box_patterns)] #![feature(box_syntax)] #![feature(slice_patterns)] diff --git a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr index e11702df80a..6673549e239 100644 --- a/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr +++ b/src/test/ui/borrowck/borrowck-vec-pattern-nesting.stderr @@ -1,5 +1,5 @@ error[E0506]: cannot assign to `vec[..]` because it is borrowed - --> $DIR/borrowck-vec-pattern-nesting.rs:21:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:20:13 | LL | [box ref _a, _, _] => { | ------ borrow of `vec[..]` occurs here @@ -8,7 +8,7 @@ LL | vec[0] = box 4; //~ ERROR cannot assign | ^^^^^^^^^^^^^^ assignment to borrowed `vec[..]` occurs here error[E0506]: cannot assign to `vec[..]` because it is borrowed - --> $DIR/borrowck-vec-pattern-nesting.rs:33:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:32:13 | LL | &mut [ref _b..] => { | ------ borrow of `vec[..]` occurs here @@ -17,7 +17,7 @@ LL | vec[0] = box 4; //~ ERROR cannot assign | ^^^^^^^^^^^^^^ assignment to borrowed `vec[..]` occurs here error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:43:14 + --> $DIR/borrowck-vec-pattern-nesting.rs:42:14 | LL | &mut [_a, //~ ERROR cannot move out | ^-- hint: to prevent move, use `ref _a` or `ref mut _a` @@ -30,7 +30,7 @@ LL | | ] => { | |_________^ cannot move out of here error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:56:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:55:13 | LL | let a = vec[0]; //~ ERROR cannot move out | ^^^^^^ @@ -39,7 +39,7 @@ LL | let a = vec[0]; //~ ERROR cannot move out | help: consider using a reference instead: `&vec[0]` error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:64:14 + --> $DIR/borrowck-vec-pattern-nesting.rs:63:14 | LL | &mut [ //~ ERROR cannot move out | ______________^ @@ -50,7 +50,7 @@ LL | | _b] => {} | hint: to prevent move, use `ref _b` or `ref mut _b` error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:69:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:68:13 | LL | let a = vec[0]; //~ ERROR cannot move out | ^^^^^^ @@ -59,7 +59,7 @@ LL | let a = vec[0]; //~ ERROR cannot move out | help: consider using a reference instead: `&vec[0]` error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:77:14 + --> $DIR/borrowck-vec-pattern-nesting.rs:76:14 | LL | &mut [_a, _b, _c] => {} //~ ERROR cannot move out | ^--^^--^^--^ @@ -70,7 +70,7 @@ LL | &mut [_a, _b, _c] => {} //~ ERROR cannot move out | cannot move out of here error[E0508]: cannot move out of type `[std::boxed::Box]`, a non-copy slice - --> $DIR/borrowck-vec-pattern-nesting.rs:81:13 + --> $DIR/borrowck-vec-pattern-nesting.rs:80:13 | LL | let a = vec[0]; //~ ERROR cannot move out | ^^^^^^ diff --git a/src/test/ui/error-codes/E0527.rs b/src/test/ui/error-codes/E0527.rs index 67d222e867e..a90ccec9cf5 100644 --- a/src/test/ui/error-codes/E0527.rs +++ b/src/test/ui/error-codes/E0527.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn main() { let r = &[1, 2, 3, 4]; match r { diff --git a/src/test/ui/error-codes/E0527.stderr b/src/test/ui/error-codes/E0527.stderr index 2060f1e69e0..1e764c18587 100644 --- a/src/test/ui/error-codes/E0527.stderr +++ b/src/test/ui/error-codes/E0527.stderr @@ -1,5 +1,5 @@ error[E0527]: pattern requires 2 elements but array has 4 - --> $DIR/E0527.rs:16:10 + --> $DIR/E0527.rs:14:10 | LL | &[a, b] => { | ^^^^^^ expected 4 elements diff --git a/src/test/ui/error-codes/E0529.rs b/src/test/ui/error-codes/E0529.rs index 5262ad7b716..2459054da89 100644 --- a/src/test/ui/error-codes/E0529.rs +++ b/src/test/ui/error-codes/E0529.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - fn main() { let r: f32 = 1.0; match r { diff --git a/src/test/ui/error-codes/E0529.stderr b/src/test/ui/error-codes/E0529.stderr index fcada000790..b2e7ae23fb0 100644 --- a/src/test/ui/error-codes/E0529.stderr +++ b/src/test/ui/error-codes/E0529.stderr @@ -1,5 +1,5 @@ error[E0529]: expected an array or slice, found `f32` - --> $DIR/E0529.rs:16:9 + --> $DIR/E0529.rs:14:9 | LL | [a, b] => { | ^^^^^^ pattern cannot match with input type `f32` diff --git a/src/test/ui/feature-gate-advanced-slice-features.rs b/src/test/ui/feature-gate-advanced-slice-features.rs deleted file mode 100644 index dc9b4e634ab..00000000000 --- a/src/test/ui/feature-gate-advanced-slice-features.rs +++ /dev/null @@ -1,22 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// gate-test-advanced_slice_patterns - -#![feature(slice_patterns)] - -fn main() { - let x = [ 1, 2, 3, 4, 5 ]; - match x { - [ xs.., 4, 5 ] => {} //~ ERROR multiple-element slice matches - [ 1, xs.., 5 ] => {} //~ ERROR multiple-element slice matches - [ 1, 2, xs.. ] => {} // OK without feature gate - } -} diff --git a/src/test/ui/feature-gate-advanced-slice-features.stderr b/src/test/ui/feature-gate-advanced-slice-features.stderr deleted file mode 100644 index 9d9e7554976..00000000000 --- a/src/test/ui/feature-gate-advanced-slice-features.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0658]: multiple-element slice matches anywhere but at the end of a slice (e.g. `[0, ..xs, 0]`) are experimental (see issue #23121) - --> $DIR/feature-gate-advanced-slice-features.rs:18:9 - | -LL | [ xs.., 4, 5 ] => {} //~ ERROR multiple-element slice matches - | ^^^^^^^^^^^^^^ - | - = help: add #![feature(advanced_slice_patterns)] to the crate attributes to enable - -error[E0658]: multiple-element slice matches anywhere but at the end of a slice (e.g. `[0, ..xs, 0]`) are experimental (see issue #23121) - --> $DIR/feature-gate-advanced-slice-features.rs:19:9 - | -LL | [ 1, xs.., 5 ] => {} //~ ERROR multiple-element slice matches - | ^^^^^^^^^^^^^^ - | - = help: add #![feature(advanced_slice_patterns)] to the crate attributes to enable - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gate-slice-patterns.rs b/src/test/ui/feature-gate-slice-patterns.rs index 625cb2d3515..fd058f65172 100644 --- a/src/test/ui/feature-gate-slice-patterns.rs +++ b/src/test/ui/feature-gate-slice-patterns.rs @@ -8,11 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that slice pattern syntax is gated by `slice_patterns` feature gate +// Test that slice pattern syntax with `..` is gated by `slice_patterns` feature gate fn main() { let x = [1, 2, 3, 4, 5]; match x { - [1, 2, xs..] => {} //~ ERROR slice pattern syntax is experimental + [1, 2, ..] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + [1, .., 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + [.., 4, 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + } + + let x = [ 1, 2, 3, 4, 5 ]; + match x { + [ xs.., 4, 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + [ 1, xs.., 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + [ 1, 2, xs.. ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized } } diff --git a/src/test/ui/feature-gate-slice-patterns.stderr b/src/test/ui/feature-gate-slice-patterns.stderr index 7c216fad933..d560dcd54ee 100644 --- a/src/test/ui/feature-gate-slice-patterns.stderr +++ b/src/test/ui/feature-gate-slice-patterns.stderr @@ -1,11 +1,51 @@ -error[E0658]: slice pattern syntax is experimental (see issue #23121) - --> $DIR/feature-gate-slice-patterns.rs:16:9 +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:16:16 | -LL | [1, 2, xs..] => {} //~ ERROR slice pattern syntax is experimental - | ^^^^^^^^^^^^ +LL | [1, 2, ..] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ | = help: add #![feature(slice_patterns)] to the crate attributes to enable -error: aborting due to previous error +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:17:13 + | +LL | [1, .., 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ + | + = help: add #![feature(slice_patterns)] to the crate attributes to enable + +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:18:10 + | +LL | [.., 4, 5] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ + | + = help: add #![feature(slice_patterns)] to the crate attributes to enable + +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:23:11 + | +LL | [ xs.., 4, 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ + | + = help: add #![feature(slice_patterns)] to the crate attributes to enable + +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:24:14 + | +LL | [ 1, xs.., 5 ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ + | + = help: add #![feature(slice_patterns)] to the crate attributes to enable + +error[E0658]: syntax for subslices in slice patterns is not yet stabilized (see issue #23121) + --> $DIR/feature-gate-slice-patterns.rs:25:17 + | +LL | [ 1, 2, xs.. ] => {} //~ ERROR syntax for subslices in slice patterns is not yet stabilized + | ^^ + | + = help: add #![feature(slice_patterns)] to the crate attributes to enable + +error: aborting due to 6 previous errors For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/mismatched_types/issue-38371.rs b/src/test/ui/mismatched_types/issue-38371.rs index b9b6b05996b..8e613d4edba 100644 --- a/src/test/ui/mismatched_types/issue-38371.rs +++ b/src/test/ui/mismatched_types/issue-38371.rs @@ -7,8 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - struct Foo { } diff --git a/src/test/ui/mismatched_types/issue-38371.stderr b/src/test/ui/mismatched_types/issue-38371.stderr index 1bf1521e39e..dd5da769075 100644 --- a/src/test/ui/mismatched_types/issue-38371.stderr +++ b/src/test/ui/mismatched_types/issue-38371.stderr @@ -1,5 +1,5 @@ error[E0308]: mismatched types - --> $DIR/issue-38371.rs:16:8 + --> $DIR/issue-38371.rs:14:8 | LL | fn foo(&foo: Foo) { //~ ERROR mismatched types | ^^^^ expected struct `Foo`, found reference @@ -9,7 +9,7 @@ LL | fn foo(&foo: Foo) { //~ ERROR mismatched types = help: did you mean `foo: &Foo`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:30:9 + --> $DIR/issue-38371.rs:28:9 | LL | fn agh(&&bar: &u32) { //~ ERROR mismatched types | ^^^^ expected u32, found reference @@ -19,7 +19,7 @@ LL | fn agh(&&bar: &u32) { //~ ERROR mismatched types = help: did you mean `bar: &u32`? error[E0308]: mismatched types - --> $DIR/issue-38371.rs:33:8 + --> $DIR/issue-38371.rs:31:8 | LL | fn bgh(&&bar: u32) { //~ ERROR mismatched types | ^^^^^ expected u32, found reference @@ -28,7 +28,7 @@ LL | fn bgh(&&bar: u32) { //~ ERROR mismatched types found type `&_` error[E0529]: expected an array or slice, found `u32` - --> $DIR/issue-38371.rs:36:9 + --> $DIR/issue-38371.rs:34:9 | LL | fn ugh(&[bar]: &u32) { //~ ERROR expected an array or slice | ^^^^^ pattern cannot match with input type `u32` diff --git a/src/test/ui/non-exhaustive-pattern-witness.rs b/src/test/ui/non-exhaustive-pattern-witness.rs index 0b12a9acbcb..dd14a10a2bc 100644 --- a/src/test/ui/non-exhaustive-pattern-witness.rs +++ b/src/test/ui/non-exhaustive-pattern-witness.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(advanced_slice_patterns)] #![feature(slice_patterns)] struct Foo { diff --git a/src/test/ui/non-exhaustive-pattern-witness.stderr b/src/test/ui/non-exhaustive-pattern-witness.stderr index 7179b4b135a..e364e822ea8 100644 --- a/src/test/ui/non-exhaustive-pattern-witness.stderr +++ b/src/test/ui/non-exhaustive-pattern-witness.stderr @@ -1,41 +1,41 @@ error[E0004]: non-exhaustive patterns: `Foo { first: false, second: Some([_, _, _, _]) }` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:20:11 + --> $DIR/non-exhaustive-pattern-witness.rs:19:11 | LL | match (Foo { first: true, second: None }) { | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ pattern `Foo { first: false, second: Some([_, _, _, _]) }` not covered error[E0004]: non-exhaustive patterns: `Red` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:36:11 + --> $DIR/non-exhaustive-pattern-witness.rs:35:11 | LL | match Color::Red { | ^^^^^^^^^^ pattern `Red` not covered error[E0004]: non-exhaustive patterns: `East`, `South` and `West` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:48:11 + --> $DIR/non-exhaustive-pattern-witness.rs:47:11 | LL | match Direction::North { | ^^^^^^^^^^^^^^^^ patterns `East`, `South` and `West` not covered error[E0004]: non-exhaustive patterns: `Second`, `Third`, `Fourth` and 8 more not covered - --> $DIR/non-exhaustive-pattern-witness.rs:59:11 + --> $DIR/non-exhaustive-pattern-witness.rs:58:11 | LL | match ExcessiveEnum::First { | ^^^^^^^^^^^^^^^^^^^^ patterns `Second`, `Third`, `Fourth` and 8 more not covered error[E0004]: non-exhaustive patterns: `CustomRGBA { a: true, .. }` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:67:11 + --> $DIR/non-exhaustive-pattern-witness.rs:66:11 | LL | match Color::Red { | ^^^^^^^^^^ pattern `CustomRGBA { a: true, .. }` not covered error[E0004]: non-exhaustive patterns: `[Second(true), Second(false)]` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:83:11 + --> $DIR/non-exhaustive-pattern-witness.rs:82:11 | LL | match *x { | ^^ pattern `[Second(true), Second(false)]` not covered error[E0004]: non-exhaustive patterns: `((), false)` not covered - --> $DIR/non-exhaustive-pattern-witness.rs:96:11 + --> $DIR/non-exhaustive-pattern-witness.rs:95:11 | LL | match ((), false) { | ^^^^^^^^^^^ pattern `((), false)` not covered diff --git a/src/test/ui/pat-slice-old-style.rs b/src/test/ui/pat-slice-old-style.rs index 4ff1e94b087..65578e76d6d 100644 --- a/src/test/ui/pat-slice-old-style.rs +++ b/src/test/ui/pat-slice-old-style.rs @@ -8,11 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] - // NB: this test was introduced in #23121 and will have to change when default match binding modes // stabilizes. +#![feature(slice_patterns)] + fn slice_pat(x: &[u8]) { // OLD! match x { diff --git a/src/test/ui/rfc-2005-default-binding-mode/slice.rs b/src/test/ui/rfc-2005-default-binding-mode/slice.rs index 40aa957242c..20ef0624bf9 100644 --- a/src/test/ui/rfc-2005-default-binding-mode/slice.rs +++ b/src/test/ui/rfc-2005-default-binding-mode/slice.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(slice_patterns)] #![feature(match_default_bindings)] +#![feature(slice_patterns)] pub fn main() { let sl: &[u8] = b"foo"; -- cgit 1.4.1-3-g733a5 From 57896abc38f56dce27ca9d4642c18f44be8db620 Mon Sep 17 00:00:00 2001 From: John Kåre Alsaker Date: Tue, 20 Mar 2018 00:48:41 +0100 Subject: Make resuming generators unsafe instead of the creation of immovable generators. Fixes #47787 --- .../src/language-features/generators.md | 20 ++++++++-------- src/liballoc/boxed.rs | 2 +- src/libcore/ops/generator.rs | 12 ++++++---- src/librustc_mir/diagnostics.rs | 10 ++++---- src/librustc_mir/transform/check_unsafety.rs | 12 ++-------- src/test/run-pass/dynamic-drop.rs | 2 +- src/test/run-pass/generator/conditional-drop.rs | 8 +++---- src/test/run-pass/generator/control-flow.rs | 2 +- src/test/run-pass/generator/drop-env.rs | 4 ++-- src/test/run-pass/generator/issue-44197.rs | 6 +++-- src/test/run-pass/generator/iterator-count.rs | 4 +++- .../run-pass/generator/live-upvar-across-yield.rs | 2 +- src/test/run-pass/generator/nested_generators.rs | 2 +- src/test/run-pass/generator/panic-drops.rs | 4 ++-- src/test/run-pass/generator/panic-safe.rs | 4 ++-- src/test/run-pass/generator/resume-after-return.rs | 4 ++-- src/test/run-pass/generator/smoke.rs | 28 +++++++++++----------- src/test/run-pass/generator/static-generators.rs | 18 +++++++------- src/test/run-pass/generator/xcrate-reachable.rs | 2 +- src/test/run-pass/generator/xcrate.rs | 6 ++--- src/test/ui/generator/borrowing.rs | 2 +- src/test/ui/generator/borrowing.stderr | 10 ++++---- src/test/ui/generator/dropck.rs | 2 +- src/test/ui/generator/sized-yield.rs | 2 +- src/test/ui/generator/sized-yield.stderr | 6 ++--- src/test/ui/generator/unsafe-immovable.rs | 17 ------------- src/test/ui/generator/unsafe-immovable.stderr | 11 --------- src/test/ui/generator/yield-while-iterating.rs | 6 ++--- .../ui/generator/yield-while-local-borrowed.rs | 6 ++--- .../ui/generator/yield-while-ref-reborrowed.rs | 6 ++--- 30 files changed, 96 insertions(+), 124 deletions(-) delete mode 100644 src/test/ui/generator/unsafe-immovable.rs delete mode 100644 src/test/ui/generator/unsafe-immovable.stderr (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/generators.md b/src/doc/unstable-book/src/language-features/generators.md index e8e2132dca2..8e888de90a9 100644 --- a/src/doc/unstable-book/src/language-features/generators.md +++ b/src/doc/unstable-book/src/language-features/generators.md @@ -36,11 +36,11 @@ fn main() { return "foo" }; - match generator.resume() { + match unsafe { generator.resume() } { GeneratorState::Yielded(1) => {} _ => panic!("unexpected value from resume"), } - match generator.resume() { + match unsafe { generator.resume() } { GeneratorState::Complete("foo") => {} _ => panic!("unexpected value from resume"), } @@ -69,9 +69,9 @@ fn main() { }; println!("1"); - generator.resume(); + unsafe { generator.resume() }; println!("3"); - generator.resume(); + unsafe { generator.resume() }; println!("5"); } ``` @@ -92,7 +92,7 @@ The `Generator` trait in `std::ops` currently looks like: pub trait Generator { type Yield; type Return; - fn resume(&mut self) -> GeneratorState; + unsafe fn resume(&mut self) -> GeneratorState; } ``` @@ -175,8 +175,8 @@ fn main() { return ret }; - generator.resume(); - generator.resume(); + unsafe { generator.resume() }; + unsafe { generator.resume() }; } ``` @@ -200,7 +200,7 @@ fn main() { type Yield = i32; type Return = &'static str; - fn resume(&mut self) -> GeneratorState { + unsafe fn resume(&mut self) -> GeneratorState { use std::mem; match mem::replace(self, __Generator::Done) { __Generator::Start(s) => { @@ -223,8 +223,8 @@ fn main() { __Generator::Start(ret) }; - generator.resume(); - generator.resume(); + unsafe { generator.resume() }; + unsafe { generator.resume() }; } ``` diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index b776556d59f..a86ab87ec18 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -892,7 +892,7 @@ impl Generator for Box { type Yield = T::Yield; type Return = T::Return; - fn resume(&mut self) -> GeneratorState { + unsafe fn resume(&mut self) -> GeneratorState { (**self).resume() } } diff --git a/src/libcore/ops/generator.rs b/src/libcore/ops/generator.rs index dc7669d195c..4b70c5398be 100644 --- a/src/libcore/ops/generator.rs +++ b/src/libcore/ops/generator.rs @@ -56,11 +56,11 @@ pub enum GeneratorState { /// return "foo" /// }; /// -/// match generator.resume() { +/// match unsafe { generator.resume() } { /// GeneratorState::Yielded(1) => {} /// _ => panic!("unexpected return from resume"), /// } -/// match generator.resume() { +/// match unsafe { generator.resume() } { /// GeneratorState::Complete("foo") => {} /// _ => panic!("unexpected return from resume"), /// } @@ -98,6 +98,10 @@ pub trait Generator { /// generator will continue executing until it either yields or returns, at /// which point this function will return. /// + /// The function is unsafe because it can be used on an immovable generator. + /// After such a call, the immovable generator must not move again, but + /// this is not enforced by the compiler. + /// /// # Return value /// /// The `GeneratorState` enum returned from this function indicates what @@ -116,7 +120,7 @@ pub trait Generator { /// been returned previously. While generator literals in the language are /// guaranteed to panic on resuming after `Complete`, this is not guaranteed /// for all implementations of the `Generator` trait. - fn resume(&mut self) -> GeneratorState; + unsafe fn resume(&mut self) -> GeneratorState; } #[unstable(feature = "generator_trait", issue = "43122")] @@ -125,7 +129,7 @@ impl<'a, T> Generator for &'a mut T { type Yield = T::Yield; type Return = T::Return; - fn resume(&mut self) -> GeneratorState { + unsafe fn resume(&mut self) -> GeneratorState { (**self).resume() } } diff --git a/src/librustc_mir/diagnostics.rs b/src/librustc_mir/diagnostics.rs index 2000ebea25d..4f36c3888b9 100644 --- a/src/librustc_mir/diagnostics.rs +++ b/src/librustc_mir/diagnostics.rs @@ -2247,7 +2247,7 @@ let mut b = || { yield (); // ...is still in scope here, when the yield occurs. println!("{}", a); }; -b.resume(); +unsafe { b.resume() }; ``` At present, it is not permitted to have a yield that occurs while a @@ -2265,7 +2265,7 @@ let mut b = || { yield (); println!("{}", a); }; -b.resume(); +unsafe { b.resume() }; ``` This is a very simple case, of course. In more complex cases, we may @@ -2283,7 +2283,7 @@ let mut b = || { yield x; // ...when this yield occurs. } }; -b.resume(); +unsafe { b.resume() }; ``` Such cases can sometimes be resolved by iterating "by value" (or using @@ -2298,7 +2298,7 @@ let mut b = || { yield x; // <-- Now yield is OK. } }; -b.resume(); +unsafe { b.resume() }; ``` If taking ownership is not an option, using indices can work too: @@ -2314,7 +2314,7 @@ let mut b = || { yield x; // <-- Now yield is OK. } }; -b.resume(); +unsafe { b.resume() }; // (*) -- Unfortunately, these temporaries are currently required. // See . diff --git a/src/librustc_mir/transform/check_unsafety.rs b/src/librustc_mir/transform/check_unsafety.rs index 86d08dec2b9..033fb493d73 100644 --- a/src/librustc_mir/transform/check_unsafety.rs +++ b/src/librustc_mir/transform/check_unsafety.rs @@ -125,21 +125,13 @@ impl<'a, 'tcx> Visitor<'tcx> for UnsafetyChecker<'a, 'tcx> { &AggregateKind::Array(..) | &AggregateKind::Tuple | &AggregateKind::Adt(..) => {} - &AggregateKind::Closure(def_id, _) => { + &AggregateKind::Closure(def_id, _) | + &AggregateKind::Generator(def_id, _, _) => { let UnsafetyCheckResult { violations, unsafe_blocks } = self.tcx.unsafety_check_result(def_id); self.register_violations(&violations, &unsafe_blocks); } - &AggregateKind::Generator(def_id, _, interior) => { - let UnsafetyCheckResult { - violations, unsafe_blocks - } = self.tcx.unsafety_check_result(def_id); - self.register_violations(&violations, &unsafe_blocks); - if !interior.movable { - self.require_unsafe("construction of immovable generator") - } - } } } self.super_rvalue(rvalue, location); diff --git a/src/test/run-pass/dynamic-drop.rs b/src/test/run-pass/dynamic-drop.rs index 4d0bd3f3412..abbce16b77c 100644 --- a/src/test/run-pass/dynamic-drop.rs +++ b/src/test/run-pass/dynamic-drop.rs @@ -178,7 +178,7 @@ fn generator(a: &Allocator, run_count: usize) { ); }; for _ in 0..run_count { - gen.resume(); + unsafe { gen.resume(); } } } diff --git a/src/test/run-pass/generator/conditional-drop.rs b/src/test/run-pass/generator/conditional-drop.rs index 8329684e1a3..3d39c46186b 100644 --- a/src/test/run-pass/generator/conditional-drop.rs +++ b/src/test/run-pass/generator/conditional-drop.rs @@ -42,9 +42,9 @@ fn t1() { }; let n = A.load(Ordering::SeqCst); - a.resume(); + unsafe { a.resume() }; assert_eq!(A.load(Ordering::SeqCst), n + 1); - a.resume(); + unsafe { a.resume() }; assert_eq!(A.load(Ordering::SeqCst), n + 1); } @@ -58,8 +58,8 @@ fn t2() { }; let n = A.load(Ordering::SeqCst); - a.resume(); + unsafe { a.resume() }; assert_eq!(A.load(Ordering::SeqCst), n); - a.resume(); + unsafe { a.resume() }; assert_eq!(A.load(Ordering::SeqCst), n + 1); } diff --git a/src/test/run-pass/generator/control-flow.rs b/src/test/run-pass/generator/control-flow.rs index 60a00b4e467..09971410e55 100644 --- a/src/test/run-pass/generator/control-flow.rs +++ b/src/test/run-pass/generator/control-flow.rs @@ -16,7 +16,7 @@ fn finish(mut amt: usize, mut t: T) -> T::Return where T: Generator { loop { - match t.resume() { + match unsafe { t.resume() } { GeneratorState::Yielded(()) => amt = amt.checked_sub(1).unwrap(), GeneratorState::Complete(ret) => { assert_eq!(amt, 0); diff --git a/src/test/run-pass/generator/drop-env.rs b/src/test/run-pass/generator/drop-env.rs index ac42a25899d..ef4dc24472e 100644 --- a/src/test/run-pass/generator/drop-env.rs +++ b/src/test/run-pass/generator/drop-env.rs @@ -37,7 +37,7 @@ fn t1() { }; let n = A.load(Ordering::SeqCst); - drop(foo.resume()); + drop(unsafe { foo.resume() }); assert_eq!(A.load(Ordering::SeqCst), n); drop(foo); assert_eq!(A.load(Ordering::SeqCst), n + 1); @@ -50,7 +50,7 @@ fn t2() { }; let n = A.load(Ordering::SeqCst); - drop(foo.resume()); + drop(unsafe { foo.resume() }); assert_eq!(A.load(Ordering::SeqCst), n + 1); drop(foo); assert_eq!(A.load(Ordering::SeqCst), n + 1); diff --git a/src/test/run-pass/generator/issue-44197.rs b/src/test/run-pass/generator/issue-44197.rs index 7cb80ea8b21..8ce4fc6affa 100644 --- a/src/test/run-pass/generator/issue-44197.rs +++ b/src/test/run-pass/generator/issue-44197.rs @@ -35,6 +35,8 @@ fn bar2(baz: String) -> impl Generator { } fn main() { - assert_eq!(bar(String::new()).resume(), GeneratorState::Yielded(String::new())); - assert_eq!(bar2(String::new()).resume(), GeneratorState::Complete(())); + unsafe { + assert_eq!(bar(String::new()).resume(), GeneratorState::Yielded(String::new())); + assert_eq!(bar2(String::new()).resume(), GeneratorState::Complete(())); + } } diff --git a/src/test/run-pass/generator/iterator-count.rs b/src/test/run-pass/generator/iterator-count.rs index 9afe95f9e86..654b18928c0 100644 --- a/src/test/run-pass/generator/iterator-count.rs +++ b/src/test/run-pass/generator/iterator-count.rs @@ -14,11 +14,13 @@ use std::ops::{GeneratorState, Generator}; struct W(T); +// This impl isn't safe in general, but the generator used in this test is movable +// so it won't cause problems. impl> Iterator for W { type Item = T::Yield; fn next(&mut self) -> Option { - match self.0.resume() { + match unsafe { self.0.resume() } { GeneratorState::Complete(..) => None, GeneratorState::Yielded(v) => Some(v), } diff --git a/src/test/run-pass/generator/live-upvar-across-yield.rs b/src/test/run-pass/generator/live-upvar-across-yield.rs index e34b0b3100c..28e7da232ce 100644 --- a/src/test/run-pass/generator/live-upvar-across-yield.rs +++ b/src/test/run-pass/generator/live-upvar-across-yield.rs @@ -17,5 +17,5 @@ fn main() { let mut a = || { b(yield); }; - a.resume(); + unsafe { a.resume() }; } diff --git a/src/test/run-pass/generator/nested_generators.rs b/src/test/run-pass/generator/nested_generators.rs index f70d4144a3c..29808da85a7 100644 --- a/src/test/run-pass/generator/nested_generators.rs +++ b/src/test/run-pass/generator/nested_generators.rs @@ -20,7 +20,7 @@ fn main() { yield 2; }; - match sub_generator.resume() { + match unsafe { sub_generator.resume() } { GeneratorState::Yielded(x) => { yield x; } diff --git a/src/test/run-pass/generator/panic-drops.rs b/src/test/run-pass/generator/panic-drops.rs index 36e401a54bc..3d7b60ab6b9 100644 --- a/src/test/run-pass/generator/panic-drops.rs +++ b/src/test/run-pass/generator/panic-drops.rs @@ -42,7 +42,7 @@ fn main() { assert_eq!(A.load(Ordering::SeqCst), 0); let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { - foo.resume() + unsafe { foo.resume() } })); assert!(res.is_err()); assert_eq!(A.load(Ordering::SeqCst), 1); @@ -57,7 +57,7 @@ fn main() { assert_eq!(A.load(Ordering::SeqCst), 1); let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { - foo.resume() + unsafe { foo.resume() } })); assert!(res.is_err()); assert_eq!(A.load(Ordering::SeqCst), 1); diff --git a/src/test/run-pass/generator/panic-safe.rs b/src/test/run-pass/generator/panic-safe.rs index 0d5bae7876b..ace5cdde51d 100644 --- a/src/test/run-pass/generator/panic-safe.rs +++ b/src/test/run-pass/generator/panic-safe.rs @@ -24,13 +24,13 @@ fn main() { }; let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { - foo.resume() + unsafe { foo.resume() } })); assert!(res.is_err()); for _ in 0..10 { let res = panic::catch_unwind(panic::AssertUnwindSafe(|| { - foo.resume() + unsafe { foo.resume() } })); assert!(res.is_err()); } diff --git a/src/test/run-pass/generator/resume-after-return.rs b/src/test/run-pass/generator/resume-after-return.rs index 56511dcd53a..06e7615d261 100644 --- a/src/test/run-pass/generator/resume-after-return.rs +++ b/src/test/run-pass/generator/resume-after-return.rs @@ -23,12 +23,12 @@ fn main() { yield; }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } - match panic::catch_unwind(move || foo.resume()) { + match panic::catch_unwind(move || unsafe { foo.resume() }) { Ok(_) => panic!("generator successfully resumed"), Err(_) => {} } diff --git a/src/test/run-pass/generator/smoke.rs b/src/test/run-pass/generator/smoke.rs index 8693964665d..7395c8484c1 100644 --- a/src/test/run-pass/generator/smoke.rs +++ b/src/test/run-pass/generator/smoke.rs @@ -24,7 +24,7 @@ fn simple() { } }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } @@ -40,7 +40,7 @@ fn return_capture() { a }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(ref s) if *s == "foo" => {} s => panic!("bad state: {:?}", s), } @@ -52,11 +52,11 @@ fn simple_yield() { yield; }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(()) => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } @@ -69,11 +69,11 @@ fn yield_capture() { yield b; }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(ref s) if *s == "foo" => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } @@ -86,11 +86,11 @@ fn simple_yield_value() { return String::from("foo") }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(ref s) if *s == "bar" => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(ref s) if *s == "foo" => {} s => panic!("bad state: {:?}", s), } @@ -104,11 +104,11 @@ fn return_after_yield() { return a }; - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(()) => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(ref s) if *s == "foo" => {} s => panic!("bad state: {:?}", s), } @@ -156,11 +156,11 @@ fn send_and_sync() { fn send_over_threads() { let mut foo = || { yield }; thread::spawn(move || { - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(()) => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } @@ -169,11 +169,11 @@ fn send_over_threads() { let a = String::from("a"); let mut foo = || { yield a }; thread::spawn(move || { - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(ref s) if *s == "a" => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } diff --git a/src/test/run-pass/generator/static-generators.rs b/src/test/run-pass/generator/static-generators.rs index 9504414d8b6..ebc070eee09 100644 --- a/src/test/run-pass/generator/static-generators.rs +++ b/src/test/run-pass/generator/static-generators.rs @@ -13,14 +13,14 @@ use std::ops::{Generator, GeneratorState}; fn main() { - let mut generator = unsafe { - static || { - let a = true; - let b = &a; - yield; - assert_eq!(b as *const _, &a as *const _); - } + let mut generator = static || { + let a = true; + let b = &a; + yield; + assert_eq!(b as *const _, &a as *const _); }; - assert_eq!(generator.resume(), GeneratorState::Yielded(())); - assert_eq!(generator.resume(), GeneratorState::Complete(())); + unsafe { + assert_eq!(generator.resume(), GeneratorState::Yielded(())); + assert_eq!(generator.resume(), GeneratorState::Complete(())); + } } diff --git a/src/test/run-pass/generator/xcrate-reachable.rs b/src/test/run-pass/generator/xcrate-reachable.rs index dff5e08b9c2..8eeb0133144 100644 --- a/src/test/run-pass/generator/xcrate-reachable.rs +++ b/src/test/run-pass/generator/xcrate-reachable.rs @@ -17,5 +17,5 @@ extern crate xcrate_reachable as foo; use std::ops::Generator; fn main() { - foo::foo().resume(); + unsafe { foo::foo().resume(); } } diff --git a/src/test/run-pass/generator/xcrate.rs b/src/test/run-pass/generator/xcrate.rs index dc7a6fdef9c..04791d51356 100644 --- a/src/test/run-pass/generator/xcrate.rs +++ b/src/test/run-pass/generator/xcrate.rs @@ -19,18 +19,18 @@ use std::ops::{GeneratorState, Generator}; fn main() { let mut foo = xcrate::foo(); - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } let mut foo = xcrate::bar(3); - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Yielded(3) => {} s => panic!("bad state: {:?}", s), } - match foo.resume() { + match unsafe { foo.resume() } { GeneratorState::Complete(()) => {} s => panic!("bad state: {:?}", s), } diff --git a/src/test/ui/generator/borrowing.rs b/src/test/ui/generator/borrowing.rs index de10bdef4ae..e56927d8182 100644 --- a/src/test/ui/generator/borrowing.rs +++ b/src/test/ui/generator/borrowing.rs @@ -15,7 +15,7 @@ use std::ops::Generator; fn main() { let _b = { let a = 3; - (|| yield &a).resume() + unsafe { (|| yield &a).resume() } //~^ ERROR: `a` does not live long enough }; diff --git a/src/test/ui/generator/borrowing.stderr b/src/test/ui/generator/borrowing.stderr index 2a5de3790ad..45d950b5aef 100644 --- a/src/test/ui/generator/borrowing.stderr +++ b/src/test/ui/generator/borrowing.stderr @@ -1,10 +1,10 @@ error[E0597]: `a` does not live long enough - --> $DIR/borrowing.rs:18:20 + --> $DIR/borrowing.rs:18:29 | -LL | (|| yield &a).resume() - | -- ^ borrowed value does not live long enough - | | - | capture occurs here +LL | unsafe { (|| yield &a).resume() } + | -- ^ borrowed value does not live long enough + | | + | capture occurs here LL | //~^ ERROR: `a` does not live long enough LL | }; | - borrowed value only lives until here diff --git a/src/test/ui/generator/dropck.rs b/src/test/ui/generator/dropck.rs index 0b143d7f514..b2240fb225f 100644 --- a/src/test/ui/generator/dropck.rs +++ b/src/test/ui/generator/dropck.rs @@ -23,6 +23,6 @@ fn main() { let _d = ref_.take(); //~ ERROR `ref_` does not live long enough yield; }; - gen.resume(); + unsafe { gen.resume(); } // drops the RefCell and then the Ref, leading to use-after-free } diff --git a/src/test/ui/generator/sized-yield.rs b/src/test/ui/generator/sized-yield.rs index f38ebf8b946..a1c8ca77e41 100644 --- a/src/test/ui/generator/sized-yield.rs +++ b/src/test/ui/generator/sized-yield.rs @@ -17,5 +17,5 @@ fn main() { let mut gen = move || { //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied yield s[..]; }; - gen.resume(); //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + unsafe { gen.resume(); } //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied } diff --git a/src/test/ui/generator/sized-yield.stderr b/src/test/ui/generator/sized-yield.stderr index fc99c7e3bd7..957fac172c2 100644 --- a/src/test/ui/generator/sized-yield.stderr +++ b/src/test/ui/generator/sized-yield.stderr @@ -11,10 +11,10 @@ LL | | }; = note: the yield type of a generator must have a statically known size error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied - --> $DIR/sized-yield.rs:20:8 + --> $DIR/sized-yield.rs:20:17 | -LL | gen.resume(); //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied - | ^^^^^^ `str` does not have a constant size known at compile-time +LL | unsafe { gen.resume(); } //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + | ^^^^^^ `str` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `str` diff --git a/src/test/ui/generator/unsafe-immovable.rs b/src/test/ui/generator/unsafe-immovable.rs deleted file mode 100644 index 45acbf50931..00000000000 --- a/src/test/ui/generator/unsafe-immovable.rs +++ /dev/null @@ -1,17 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(generators)] - -fn main() { - static || { //~ ERROR: construction of immovable generator requires unsafe - yield; - }; -} diff --git a/src/test/ui/generator/unsafe-immovable.stderr b/src/test/ui/generator/unsafe-immovable.stderr deleted file mode 100644 index b2add55613d..00000000000 --- a/src/test/ui/generator/unsafe-immovable.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0133]: construction of immovable generator requires unsafe function or block - --> $DIR/unsafe-immovable.rs:14:5 - | -LL | / static || { //~ ERROR: construction of immovable generator requires unsafe -LL | | yield; -LL | | }; - | |_____^ construction of immovable generator - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0133`. diff --git a/src/test/ui/generator/yield-while-iterating.rs b/src/test/ui/generator/yield-while-iterating.rs index bc53448cb08..b8a67a0e7b6 100644 --- a/src/test/ui/generator/yield-while-iterating.rs +++ b/src/test/ui/generator/yield-while-iterating.rs @@ -43,7 +43,7 @@ fn yield_during_iter_borrowed_slice_2() { println!("{:?}", x); } -fn yield_during_iter_borrowed_slice_3() { +unsafe fn yield_during_iter_borrowed_slice_3() { // OK to take a mutable ref to `x` and yield // up pointers from it: let mut x = vec![22_i32]; @@ -55,7 +55,7 @@ fn yield_during_iter_borrowed_slice_3() { b.resume(); } -fn yield_during_iter_borrowed_slice_4() { +unsafe fn yield_during_iter_borrowed_slice_4() { // ...but not OK to do that while reading // from `x` too let mut x = vec![22_i32]; @@ -68,7 +68,7 @@ fn yield_during_iter_borrowed_slice_4() { b.resume(); } -fn yield_during_range_iter() { +unsafe fn yield_during_range_iter() { // Should be OK. let mut b = || { let v = vec![1,2,3]; diff --git a/src/test/ui/generator/yield-while-local-borrowed.rs b/src/test/ui/generator/yield-while-local-borrowed.rs index 11bd4ed05ca..3dc2650a2ec 100644 --- a/src/test/ui/generator/yield-while-local-borrowed.rs +++ b/src/test/ui/generator/yield-while-local-borrowed.rs @@ -15,7 +15,7 @@ use std::ops::{GeneratorState, Generator}; use std::cell::Cell; -fn borrow_local_inline() { +unsafe fn borrow_local_inline() { // Not OK to yield with a borrow of a temporary. // // (This error occurs because the region shows up in the type of @@ -30,7 +30,7 @@ fn borrow_local_inline() { b.resume(); } -fn borrow_local_inline_done() { +unsafe fn borrow_local_inline_done() { // No error here -- `a` is not in scope at the point of `yield`. let mut b = move || { { @@ -41,7 +41,7 @@ fn borrow_local_inline_done() { b.resume(); } -fn borrow_local() { +unsafe fn borrow_local() { // Not OK to yield with a borrow of a temporary. // // (This error occurs because the region shows up in the type of diff --git a/src/test/ui/generator/yield-while-ref-reborrowed.rs b/src/test/ui/generator/yield-while-ref-reborrowed.rs index b9c963ae740..573dd4377bb 100644 --- a/src/test/ui/generator/yield-while-ref-reborrowed.rs +++ b/src/test/ui/generator/yield-while-ref-reborrowed.rs @@ -13,7 +13,7 @@ use std::ops::{GeneratorState, Generator}; use std::cell::Cell; -fn reborrow_shared_ref(x: &i32) { +unsafe fn reborrow_shared_ref(x: &i32) { // This is OK -- we have a borrow live over the yield, but it's of // data that outlives the generator. let mut b = move || { @@ -24,7 +24,7 @@ fn reborrow_shared_ref(x: &i32) { b.resume(); } -fn reborrow_mutable_ref(x: &mut i32) { +unsafe fn reborrow_mutable_ref(x: &mut i32) { // This is OK -- we have a borrow live over the yield, but it's of // data that outlives the generator. let mut b = move || { @@ -35,7 +35,7 @@ fn reborrow_mutable_ref(x: &mut i32) { b.resume(); } -fn reborrow_mutable_ref_2(x: &mut i32) { +unsafe fn reborrow_mutable_ref_2(x: &mut i32) { // ...but not OK to go on using `x`. let mut b = || { let a = &mut *x; -- cgit 1.4.1-3-g733a5 From 7c442e5c9b46de225f8903352b7dfc5552b297de Mon Sep 17 00:00:00 2001 From: Murarth Date: Wed, 21 Mar 2018 10:47:03 -0700 Subject: Stabilize method `String::retain` --- .../src/library-features/string-retain.md | 23 ---------------------- src/liballoc/string.rs | 4 +--- 2 files changed, 1 insertion(+), 26 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/string-retain.md (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/library-features/string-retain.md b/src/doc/unstable-book/src/library-features/string-retain.md deleted file mode 100644 index 049444aa49b..00000000000 --- a/src/doc/unstable-book/src/library-features/string-retain.md +++ /dev/null @@ -1,23 +0,0 @@ -# `string_retain` - -The tracking issue for this feature is: [#43874] - -[#43874]: https://github.com/rust-lang/rust/issues/43874 - ------------------------- - -Retains only the characters specified by the predicate. - -In other words, remove all characters `c` such that `f(c)` returns `false`. -This method operates in place and preserves the order of the retained -characters. - -```rust -#![feature(string_retain)] - -let mut s = String::from("f_o_ob_ar"); - -s.retain(|c| c != '_'); - -assert_eq!(s, "foobar"); -``` diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 9fec9091498..52ff6357c5b 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1177,8 +1177,6 @@ impl String { /// # Examples /// /// ``` - /// #![feature(string_retain)] - /// /// let mut s = String::from("f_o_ob_ar"); /// /// s.retain(|c| c != '_'); @@ -1186,7 +1184,7 @@ impl String { /// assert_eq!(s, "foobar"); /// ``` #[inline] - #[unstable(feature = "string_retain", issue = "43874")] + #[stable(feature = "string_retain", since = "1.26.0")] pub fn retain(&mut self, mut f: F) where F: FnMut(char) -> bool { -- cgit 1.4.1-3-g733a5 From fbec3ec5a78747ee458518e4be7cfe1b5eac9e3b Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Mon, 25 Dec 2017 00:00:04 +0000 Subject: Implement get_key_value for HashMap, BTreeMap --- src/liballoc/btree/map.rs | 27 +++++++++++++++++++++++++++ src/libstd/collections/hash/map.rs | 28 ++++++++++++++++++++++++++++ 2 files changed, 55 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index ed9c8c18f0d..cada190032a 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -576,6 +576,33 @@ impl BTreeMap { } } + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_get_key_value)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); + /// assert_eq!(map.get_key_value(&2), None); + /// ``` + #[unstable(feature = "map_get_key_value", issue = "49347")] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where K: Borrow, + Q: Ord + { + match search::search_tree(self.root.as_ref(), k) { + Found(handle) => Some(handle.into_kv()), + GoDown(_) => None, + } + } + /// Returns `true` if the map contains a value for the specified key. /// /// The key may be any borrowed form of the map's key type, but the ordering diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index b18b38ec302..f0bb781411f 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -1184,6 +1184,34 @@ impl HashMap self.search(k).map(|bucket| bucket.into_refs().1) } + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map's key type, but + /// [`Hash`] and [`Eq`] on the borrowed form *must* match those for + /// the key type. + /// + /// [`Eq`]: ../../std/cmp/trait.Eq.html + /// [`Hash`]: ../../std/hash/trait.Hash.html + /// + /// # Examples + /// + /// ``` + /// #![feature(map_get_key_value)] + /// use std::collections::HashMap; + /// + /// let mut map = HashMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); + /// assert_eq!(map.get_key_value(&2), None); + /// ``` + #[unstable(feature = "map_get_key_value", issue = "49347")] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where K: Borrow, + Q: Hash + Eq + { + self.search(k).map(|bucket| bucket.into_refs()) + } + /// Returns true if the map contains a value for the specified key. /// /// The key may be any borrowed form of the map's key type, but -- cgit 1.4.1-3-g733a5 From 1e2458e1baf987ee67b4c48c0583dfad65f7dcd7 Mon Sep 17 00:00:00 2001 From: boats Date: Mon, 26 Mar 2018 06:25:31 -0700 Subject: Add is_whitespace and is_alphanumeric to str. The other methods from `UnicodeStr` are already stable inherent methods on str, but these have not been included. --- src/liballoc/str.rs | 42 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 42 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 14d5e96d2e7..d5ef41df0d8 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -2122,6 +2122,48 @@ impl str { unsafe { String::from_utf8_unchecked(buf) } } + /// Returns true if this `str` is entirely whitespace, and false otherwise. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived Core + /// Property `White_Space`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert!(" \t ".is_whitespace()); + /// + /// // a non-breaking space + /// assert!("\u{A0}".is_whitespace()); + /// + /// assert!(!" 越".is_whitespace()); + /// ``` + #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] + #[inline] + pub fn is_whitespace(&self) -> bool { + UnicodeStr::is_whitespace(self) + } + + /// Returns true if this `str` is entirely alphanumeric, and false otherwise. + /// + /// 'Alphanumeric'-ness is defined in terms of the Unicode General Categories + /// 'Nd', 'Nl', 'No' and the Derived Core Property 'Alphabetic'. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert!("٣7৬Kو藏".is_alphanumeric()); + /// assert!(!"¾①".is_alphanumeric()); + /// ``` + #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] + #[inline] + pub fn is_alphanumeric(&self) -> bool { + UnicodeStr::is_alphanumeric(self) + } + /// Checks if all characters in this string are within the ASCII range. /// /// # Examples -- cgit 1.4.1-3-g733a5 From 7ce8191775b44d3773e28d647b5b17ec85508e16 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Fri, 16 Mar 2018 19:51:49 -0500 Subject: Stabilize i128_type --- src/liballoc/benches/lib.rs | 2 +- src/liballoc/lib.rs | 2 +- src/libcore/lib.rs | 2 +- src/libcore/num/mod.rs | 6 ++-- src/libcore/tests/lib.rs | 2 +- src/libproc_macro/lib.rs | 2 +- src/librustc/lib.rs | 2 +- src/librustc_apfloat/lib.rs | 2 +- src/librustc_apfloat/tests/ieee.rs | 2 +- src/librustc_const_eval/lib.rs | 2 +- src/librustc_const_math/lib.rs | 2 +- src/librustc_data_structures/lib.rs | 2 +- src/librustc_errors/lib.rs | 2 +- src/librustc_incremental/lib.rs | 2 +- src/librustc_lint/lib.rs | 2 +- src/librustc_metadata/lib.rs | 2 +- src/librustc_mir/lib.rs | 2 +- src/librustc_resolve/lib.rs | 11 -------- src/librustc_trans/lib.rs | 2 +- src/librustc_trans_utils/lib.rs | 2 +- src/librustc_typeck/lib.rs | 2 +- src/libserialize/lib.rs | 2 +- src/libstd/lib.rs | 2 +- src/libsyntax/feature_gate.rs | 17 ++---------- src/libsyntax/lib.rs | 2 +- src/libsyntax_pos/lib.rs | 2 +- src/test/codegen/unchecked-float-casts.rs | 1 - src/test/mir-opt/lower_128bit_debug_test.rs | 1 - src/test/mir-opt/lower_128bit_test.rs | 1 - src/test/run-pass/float-int-invalid-const-cast.rs | 1 - src/test/run-pass/i128-ffi.rs | 2 -- src/test/run-pass/i128.rs | 2 +- src/test/run-pass/intrinsics-integer.rs | 2 +- src/test/run-pass/issue-38763.rs | 2 -- src/test/run-pass/issue-38987.rs | 1 - .../run-pass/next-power-of-two-overflow-debug.rs | 2 -- .../run-pass/next-power-of-two-overflow-ndebug.rs | 2 -- src/test/run-pass/saturating-float-casts.rs | 2 +- src/test/run-pass/u128-as-f32.rs | 2 +- src/test/run-pass/u128.rs | 2 +- src/test/ui/feature-gate-i128_type.rs | 18 ------------ src/test/ui/feature-gate-i128_type.stderr | 19 ------------- src/test/ui/feature-gate-i128_type2.rs | 8 +++--- src/test/ui/feature-gate-i128_type2.stderr | 32 ---------------------- src/test/ui/lint-ctypes.rs | 2 +- src/test/ui/lint/type-overflow.rs | 2 -- 46 files changed, 37 insertions(+), 147 deletions(-) delete mode 100644 src/test/ui/feature-gate-i128_type.rs delete mode 100644 src/test/ui/feature-gate-i128_type.stderr (limited to 'src/liballoc') diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 2de0ffb4b26..09685d1bb40 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -10,7 +10,7 @@ #![deny(warnings)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(rand)] #![feature(repr_simd)] #![feature(test)] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f914b1a93a9..19d64d8fea9 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -97,7 +97,7 @@ #![feature(from_ref)] #![feature(fundamental)] #![feature(generic_param_attrs)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 9aebe2e4ee4..11fecde3951 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -78,7 +78,7 @@ #![feature(doc_spotlight)] #![feature(fn_must_use)] #![feature(fundamental)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(intrinsics)] #![feature(iterator_flatten)] diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 18e0aa453d8..9ff42a6d4ea 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -1635,8 +1635,7 @@ impl i64 { #[lang = "i128"] impl i128 { int_impl! { i128, i128, u128, 128, -170141183460469231731687303715884105728, - 170141183460469231731687303715884105727, "#![feature(i128_type)] -#![feature(i128)] + 170141183460469231731687303715884105727, "#![feature(i128)] # fn main() { ", " # }" } @@ -3493,8 +3492,7 @@ impl u64 { #[lang = "u128"] impl u128 { - uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "#![feature(i128_type)] -#![feature(i128)] + uint_impl! { u128, u128, 128, 340282366920938463463374607431768211455, "#![feature(i128)] # fn main() { ", " diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 1c71669abb1..0b70f692403 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -23,7 +23,7 @@ #![feature(fmt_internals)] #![feature(hashmap_internals)] #![feature(iterator_step_by)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(iterator_try_fold)] #![feature(iterator_flatten)] diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index d6e679bad48..716a2cc6cbb 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -34,7 +34,7 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(rustc_private)] #![feature(staged_api)] #![feature(lang_items)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 1bb903c0627..061044cdf14 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -53,7 +53,7 @@ #![feature(from_ref)] #![feature(fs_read_write)] #![feature(i128)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![cfg_attr(windows, feature(libc))] #![feature(match_default_bindings)] diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 565658804b0..2ee7bea8476 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -46,8 +46,8 @@ #![deny(warnings)] #![forbid(unsafe_code)] -#![feature(i128_type)] #![cfg_attr(stage0, feature(slice_patterns))] +#![cfg_attr(stage0, feature(i128_type))] #![feature(try_from)] // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/librustc_apfloat/tests/ieee.rs b/src/librustc_apfloat/tests/ieee.rs index ff46ee79c31..627d79724b2 100644 --- a/src/librustc_apfloat/tests/ieee.rs +++ b/src/librustc_apfloat/tests/ieee.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #[macro_use] extern crate rustc_apfloat; diff --git a/src/librustc_const_eval/lib.rs b/src/librustc_const_eval/lib.rs index 2b0775e8695..2620448927d 100644 --- a/src/librustc_const_eval/lib.rs +++ b/src/librustc_const_eval/lib.rs @@ -23,7 +23,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(macro_lifetime_matcher)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(from_ref)] extern crate arena; diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 5555e727a95..a53055c7ce7 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -20,7 +20,7 @@ #![deny(warnings)] #![feature(i128)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] extern crate rustc_apfloat; diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index ff869072871..01f91e37db8 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -26,7 +26,7 @@ #![feature(unboxed_closures)] #![feature(fn_traits)] #![feature(unsize)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(i128)] #![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(specialization)] diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 1152c9c574e..37ae64cef57 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -18,7 +18,7 @@ #![feature(range_contains)] #![cfg_attr(unix, feature(libc))] #![cfg_attr(stage0, feature(conservative_impl_trait))] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(optin_builtin_traits)] extern crate atty; diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 6adb950fe4e..5a33f566e90 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -17,7 +17,7 @@ #![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(fs_read_write)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(specialization)] diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index 4639f7b2d28..d024adad9d0 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -27,7 +27,7 @@ #![cfg_attr(test, feature(test))] #![feature(box_patterns)] #![feature(box_syntax)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(macro_vis_matcher)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 902dd87c574..4af5ec9ae08 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -16,7 +16,7 @@ #![feature(box_patterns)] #![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(fs_read_write)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(libc)] #![feature(macro_lifetime_matcher)] #![feature(proc_macro_internals)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 750839f8b00..a1f096b2a38 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -27,7 +27,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(decl_macro)] #![feature(dyn_trait)] #![feature(fs_read_write)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(macro_vis_matcher)] #![feature(match_default_bindings)] diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2cb2c76c632..01eda71e9b6 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -3117,17 +3117,6 @@ impl<'a> Resolver<'a> { self.primitive_type_table.primitive_types .contains_key(&path[0].node.name) => { let prim = self.primitive_type_table.primitive_types[&path[0].node.name]; - match prim { - TyUint(UintTy::U128) | TyInt(IntTy::I128) => { - if !self.session.features_untracked().i128_type { - emit_feature_err(&self.session.parse_sess, - "i128_type", span, GateIssue::Language, - "128-bit type is unstable"); - - } - } - _ => {} - } PathResolution::with_unresolved_segments(Def::PrimTy(prim), path.len() - 1) } PathResult::Module(module) => PathResolution::new(module.def().unwrap()), diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 38adc603628..d89d19db63e 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -24,7 +24,7 @@ #![feature(custom_attribute)] #![feature(fs_read_write)] #![allow(unused_attributes)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(i128)] #![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(libc)] diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 9e4addd1ed1..99de124c6e1 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -21,7 +21,7 @@ #![feature(box_syntax)] #![feature(custom_attribute)] #![allow(unused_attributes)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(quote)] #![feature(rustc_diagnostic_macros)] #![cfg_attr(stage0, feature(conservative_impl_trait))] diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index e466ef39234..8b3d5af3edd 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -86,7 +86,7 @@ This API is completely unstable and subject to change. #![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![cfg_attr(stage0, feature(never_type))] #[macro_use] extern crate log; diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index 2e354252c15..ee952523462 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -23,7 +23,7 @@ Core encoding and decoding interfaces. #![feature(box_syntax)] #![feature(core_intrinsics)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(specialization)] #![cfg_attr(test, feature(test))] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 0b06c5d4d65..3cc5d7b81c3 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -270,7 +270,7 @@ #![feature(hashmap_internals)] #![feature(heap_api)] #![feature(i128)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(int_error_internals)] #![feature(integer_atomics)] #![feature(into_cow)] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1bb369b551d..4e3c77d5e46 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -303,9 +303,6 @@ declare_features! ( // `extern "ptx-*" fn()` (active, abi_ptx, "1.15.0", None, None), - // The `i128` type - (active, i128_type, "1.16.0", Some(35118), None), - // The `repr(i128)` annotation for enums (active, repr128, "1.16.0", Some(35118), None), @@ -564,6 +561,8 @@ declare_features! ( (accepted, universal_impl_trait, "1.26.0", Some(34511), None), // Allows `impl Trait` in function return types. (accepted, conservative_impl_trait, "1.26.0", Some(34511), None), + // The `i128` type + (accepted, i128_type, "1.26.0", Some(35118), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1641,18 +1640,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { e.span, "yield syntax is experimental"); } - ast::ExprKind::Lit(ref lit) => { - if let ast::LitKind::Int(_, ref ty) = lit.node { - match *ty { - ast::LitIntType::Signed(ast::IntTy::I128) | - ast::LitIntType::Unsigned(ast::UintTy::U128) => { - gate_feature_post!(&self, i128_type, e.span, - "128-bit integers are not stable"); - } - _ => {} - } - } - } ast::ExprKind::Catch(_) => { gate_feature_post!(&self, catch_expr, e.span, "`catch` expression is experimental"); } diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 74f1ee373ec..2218b396685 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -24,7 +24,7 @@ #![feature(rustc_diagnostic_macros)] #![feature(match_default_bindings)] #![feature(non_exhaustive)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(const_atomic_usize_new)] #![feature(rustc_attrs)] diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 5a7b7e9ceca..eb345200f41 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -21,7 +21,7 @@ #![feature(const_fn)] #![feature(custom_attribute)] -#![feature(i128_type)] +#![cfg_attr(stage0, feature(i128_type))] #![feature(optin_builtin_traits)] #![allow(unused_attributes)] #![feature(specialization)] diff --git a/src/test/codegen/unchecked-float-casts.rs b/src/test/codegen/unchecked-float-casts.rs index c2fc2966170..87ebaaeec32 100644 --- a/src/test/codegen/unchecked-float-casts.rs +++ b/src/test/codegen/unchecked-float-casts.rs @@ -14,7 +14,6 @@ // -Z saturating-float-casts is not enabled. #![crate_type = "lib"] -#![feature(i128_type)] // CHECK-LABEL: @f32_to_u32 #[no_mangle] diff --git a/src/test/mir-opt/lower_128bit_debug_test.rs b/src/test/mir-opt/lower_128bit_debug_test.rs index 1752445a141..d7586b1aa4b 100644 --- a/src/test/mir-opt/lower_128bit_debug_test.rs +++ b/src/test/mir-opt/lower_128bit_debug_test.rs @@ -15,7 +15,6 @@ // compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=yes -#![feature(i128_type)] #![feature(const_fn)] static TEST_SIGNED: i128 = const_signed(-222); diff --git a/src/test/mir-opt/lower_128bit_test.rs b/src/test/mir-opt/lower_128bit_test.rs index 4058eaef9b0..341682debeb 100644 --- a/src/test/mir-opt/lower_128bit_test.rs +++ b/src/test/mir-opt/lower_128bit_test.rs @@ -15,7 +15,6 @@ // compile-flags: -Z lower_128bit_ops=yes -C debug_assertions=no -#![feature(i128_type)] #![feature(const_fn)] static TEST_SIGNED: i128 = const_signed(-222); diff --git a/src/test/run-pass/float-int-invalid-const-cast.rs b/src/test/run-pass/float-int-invalid-const-cast.rs index d44f78922c7..f84432abbfa 100644 --- a/src/test/run-pass/float-int-invalid-const-cast.rs +++ b/src/test/run-pass/float-int-invalid-const-cast.rs @@ -10,7 +10,6 @@ // ignore-emscripten no i128 support -#![feature(i128_type)] #![deny(const_err)] use std::{f32, f64}; diff --git a/src/test/run-pass/i128-ffi.rs b/src/test/run-pass/i128-ffi.rs index d989210dd71..edf278cbf64 100644 --- a/src/test/run-pass/i128-ffi.rs +++ b/src/test/run-pass/i128-ffi.rs @@ -15,8 +15,6 @@ // ignore-windows // ignore-32bit -#![feature(i128_type)] - #[link(name = "rust_test_helpers", kind = "static")] extern "C" { fn identity(f: u128) -> u128; diff --git a/src/test/run-pass/i128.rs b/src/test/run-pass/i128.rs index c3e43c92590..baf3b339984 100644 --- a/src/test/run-pass/i128.rs +++ b/src/test/run-pass/i128.rs @@ -12,7 +12,7 @@ // compile-flags: -Z borrowck=compare -#![feature(i128_type, test)] +#![feature(test)] extern crate test; use test::black_box as b; diff --git a/src/test/run-pass/intrinsics-integer.rs b/src/test/run-pass/intrinsics-integer.rs index cdfad51e648..7a8ff1befc7 100644 --- a/src/test/run-pass/intrinsics-integer.rs +++ b/src/test/run-pass/intrinsics-integer.rs @@ -10,7 +10,7 @@ // ignore-emscripten no i128 support -#![feature(intrinsics, i128_type)] +#![feature(intrinsics)] mod rusti { extern "rust-intrinsic" { diff --git a/src/test/run-pass/issue-38763.rs b/src/test/run-pass/issue-38763.rs index 01cc8265a39..e038062ff9a 100644 --- a/src/test/run-pass/issue-38763.rs +++ b/src/test/run-pass/issue-38763.rs @@ -10,8 +10,6 @@ // ignore-emscripten -#![feature(i128_type)] - #[repr(C)] pub struct Foo(i128); diff --git a/src/test/run-pass/issue-38987.rs b/src/test/run-pass/issue-38987.rs index a513476d4a3..31a3b7233d8 100644 --- a/src/test/run-pass/issue-38987.rs +++ b/src/test/run-pass/issue-38987.rs @@ -7,7 +7,6 @@ // , at your // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(i128_type)] fn main() { let _ = -0x8000_0000_0000_0000_0000_0000_0000_0000i128; diff --git a/src/test/run-pass/next-power-of-two-overflow-debug.rs b/src/test/run-pass/next-power-of-two-overflow-debug.rs index 599c6dfd31d..2135b3f8764 100644 --- a/src/test/run-pass/next-power-of-two-overflow-debug.rs +++ b/src/test/run-pass/next-power-of-two-overflow-debug.rs @@ -12,8 +12,6 @@ // ignore-wasm32-bare compiled with panic=abort by default // ignore-emscripten dies with an LLVM error -#![feature(i128_type)] - use std::panic; fn main() { diff --git a/src/test/run-pass/next-power-of-two-overflow-ndebug.rs b/src/test/run-pass/next-power-of-two-overflow-ndebug.rs index f2312b70be6..b05c1863d90 100644 --- a/src/test/run-pass/next-power-of-two-overflow-ndebug.rs +++ b/src/test/run-pass/next-power-of-two-overflow-ndebug.rs @@ -11,8 +11,6 @@ // compile-flags: -C debug_assertions=no // ignore-emscripten dies with an LLVM error -#![feature(i128_type)] - fn main() { for i in 129..256 { assert_eq!((i as u8).next_power_of_two(), 0); diff --git a/src/test/run-pass/saturating-float-casts.rs b/src/test/run-pass/saturating-float-casts.rs index c8fa49c62a0..d1a0901bb3d 100644 --- a/src/test/run-pass/saturating-float-casts.rs +++ b/src/test/run-pass/saturating-float-casts.rs @@ -11,7 +11,7 @@ // Tests saturating float->int casts. See u128-as-f32.rs for the opposite direction. // compile-flags: -Z saturating-float-casts -#![feature(test, i128, i128_type, stmt_expr_attributes)] +#![feature(test, i128, stmt_expr_attributes)] #![deny(overflowing_literals)] extern crate test; diff --git a/src/test/run-pass/u128-as-f32.rs b/src/test/run-pass/u128-as-f32.rs index 117e520155f..3531a961bef 100644 --- a/src/test/run-pass/u128-as-f32.rs +++ b/src/test/run-pass/u128-as-f32.rs @@ -10,7 +10,7 @@ // ignore-emscripten u128 not supported -#![feature(test, i128, i128_type)] +#![feature(test, i128)] #![deny(overflowing_literals)] extern crate test; diff --git a/src/test/run-pass/u128.rs b/src/test/run-pass/u128.rs index ebd43a86033..d649b3b74d3 100644 --- a/src/test/run-pass/u128.rs +++ b/src/test/run-pass/u128.rs @@ -12,7 +12,7 @@ // compile-flags: -Z borrowck=compare -#![feature(i128_type, test)] +#![feature(test)] extern crate test; use test::black_box as b; diff --git a/src/test/ui/feature-gate-i128_type.rs b/src/test/ui/feature-gate-i128_type.rs deleted file mode 100644 index ddb49a3e5d9..00000000000 --- a/src/test/ui/feature-gate-i128_type.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn test2() { - 0i128; //~ ERROR 128-bit integers are not stable -} - -fn test2_2() { - 0u128; //~ ERROR 128-bit integers are not stable -} - diff --git a/src/test/ui/feature-gate-i128_type.stderr b/src/test/ui/feature-gate-i128_type.stderr deleted file mode 100644 index eb3b29f4f55..00000000000 --- a/src/test/ui/feature-gate-i128_type.stderr +++ /dev/null @@ -1,19 +0,0 @@ -error[E0658]: 128-bit integers are not stable (see issue #35118) - --> $DIR/feature-gate-i128_type.rs:12:5 - | -LL | 0i128; //~ ERROR 128-bit integers are not stable - | ^^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - -error[E0658]: 128-bit integers are not stable (see issue #35118) - --> $DIR/feature-gate-i128_type.rs:16:5 - | -LL | 0u128; //~ ERROR 128-bit integers are not stable - | ^^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - -error: aborting due to 2 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gate-i128_type2.rs b/src/test/ui/feature-gate-i128_type2.rs index 8a7d316ed83..cd65b9d9223 100644 --- a/src/test/ui/feature-gate-i128_type2.rs +++ b/src/test/ui/feature-gate-i128_type2.rs @@ -10,20 +10,20 @@ // gate-test-i128_type -fn test1() -> i128 { //~ ERROR 128-bit type is unstable +fn test1() -> i128 { 0 } -fn test1_2() -> u128 { //~ ERROR 128-bit type is unstable +fn test1_2() -> u128 { 0 } fn test3() { - let x: i128 = 0; //~ ERROR 128-bit type is unstable + let x: i128 = 0; } fn test3_2() { - let x: u128 = 0; //~ ERROR 128-bit type is unstable + let x: u128 = 0; } #[repr(u128)] diff --git a/src/test/ui/feature-gate-i128_type2.stderr b/src/test/ui/feature-gate-i128_type2.stderr index 23d4d6c98d9..fe4557899ac 100644 --- a/src/test/ui/feature-gate-i128_type2.stderr +++ b/src/test/ui/feature-gate-i128_type2.stderr @@ -1,35 +1,3 @@ -error[E0658]: 128-bit type is unstable (see issue #35118) - --> $DIR/feature-gate-i128_type2.rs:13:15 - | -LL | fn test1() -> i128 { //~ ERROR 128-bit type is unstable - | ^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - -error[E0658]: 128-bit type is unstable (see issue #35118) - --> $DIR/feature-gate-i128_type2.rs:17:17 - | -LL | fn test1_2() -> u128 { //~ ERROR 128-bit type is unstable - | ^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - -error[E0658]: 128-bit type is unstable (see issue #35118) - --> $DIR/feature-gate-i128_type2.rs:22:12 - | -LL | let x: i128 = 0; //~ ERROR 128-bit type is unstable - | ^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - -error[E0658]: 128-bit type is unstable (see issue #35118) - --> $DIR/feature-gate-i128_type2.rs:26:12 - | -LL | let x: u128 = 0; //~ ERROR 128-bit type is unstable - | ^^^^ - | - = help: add #![feature(i128_type)] to the crate attributes to enable - error[E0658]: repr with 128-bit type is unstable (see issue #35118) --> $DIR/feature-gate-i128_type2.rs:30:1 | diff --git a/src/test/ui/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs index 77cb1ef0f51..85957831653 100644 --- a/src/test/ui/lint-ctypes.rs +++ b/src/test/ui/lint-ctypes.rs @@ -9,7 +9,7 @@ // except according to those terms. #![deny(improper_ctypes)] -#![feature(libc, i128_type, repr_transparent)] +#![feature(libc, repr_transparent)] extern crate libc; diff --git a/src/test/ui/lint/type-overflow.rs b/src/test/ui/lint/type-overflow.rs index 495989587e5..30e6fb2883b 100644 --- a/src/test/ui/lint/type-overflow.rs +++ b/src/test/ui/lint/type-overflow.rs @@ -10,8 +10,6 @@ // must-compile-successfully -#![feature(i128_type)] - fn main() { let error = 255i8; //~WARNING literal out of range for i8 -- cgit 1.4.1-3-g733a5 From 9c7b69e17909ceb090a1c4b8882a4e0924a2a755 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 26 Mar 2018 22:24:03 +0100 Subject: Remove mentions of unstable sort_by_cached key from stable documentation --- src/liballoc/slice.rs | 8 -------- 1 file changed, 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 2b4ce9fe49c..68f2313843c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1306,10 +1306,6 @@ impl [T] { /// This sort is stable (i.e. does not reorder equal elements) and `O(m n log(m n))` /// worst-case, where the key function is `O(m)`. /// - /// For expensive key functions (e.g. functions that are not simple property accesses or - /// basic operations), [`sort_by_cached_key`](#method.sort_by_cached_key) is likely to be - /// significantly faster, as it does not recompute element keys. - /// /// When applicable, unstable sorting is preferred because it is generally faster than stable /// sorting and it doesn't allocate auxiliary memory. /// See [`sort_unstable_by_key`](#method.sort_unstable_by_key). @@ -1496,10 +1492,6 @@ impl [T] { /// randomization to avoid degenerate cases, but with a fixed seed to always provide /// deterministic behavior. /// - /// Due to its key calling strategy, [`sort_unstable_by_key`](#method.sort_unstable_by_key) - /// is likely to be slower than [`sort_by_cached_key`](#method.sort_by_cached_key) in - /// cases where the key function is expensive. - /// /// # Examples /// /// ``` -- cgit 1.4.1-3-g733a5 From 04f6692aaf78809c041ba6145bde2dcbeec9725e Mon Sep 17 00:00:00 2001 From: Diggory Blake Date: Mon, 26 Mar 2018 23:24:31 +0100 Subject: Implement `shrink_to` method on collections --- src/liballoc/binary_heap.rs | 25 ++++++++++++++++++++++++ src/liballoc/string.rs | 28 ++++++++++++++++++++++++++ src/liballoc/vec.rs | 27 ++++++++++++++++++++++++- src/liballoc/vec_deque.rs | 35 ++++++++++++++++++++++++++++++++- src/libstd/collections/hash/map.rs | 40 ++++++++++++++++++++++++++++++++++++++ src/libstd/collections/hash/set.rs | 28 ++++++++++++++++++++++++++ src/libstd/ffi/os_str.rs | 30 ++++++++++++++++++++++++++++ src/libstd/lib.rs | 1 + src/libstd/sys/redox/os_str.rs | 5 +++++ src/libstd/sys/unix/os_str.rs | 5 +++++ src/libstd/sys/wasm/os_str.rs | 5 +++++ src/libstd/sys/windows/os_str.rs | 5 +++++ src/libstd/sys_common/wtf8.rs | 5 +++++ 13 files changed, 237 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index 8aaac5d6e08..f6a666b599b 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -509,6 +509,31 @@ impl BinaryHeap { self.data.shrink_to_fit(); } + /// Discards capacity with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to(10); + /// assert!(heap.capacity() >= 10); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.data.shrink_to(min_capacity) + } + /// Removes the greatest item from the binary heap and returns it, or `None` if it /// is empty. /// diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index e253122ffd6..2bb60a50679 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1015,6 +1015,34 @@ impl String { self.vec.shrink_to_fit() } + /// Shrinks the capacity of this `String` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// let mut s = String::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to(10); + /// assert!(s.capacity() >= 10); + /// s.shrink_to(0); + /// assert!(s.capacity() >= 3); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.vec.shrink_to(min_capacity) + } + /// Appends the given [`char`] to the end of this `String`. /// /// [`char`]: ../../std/primitive.char.html diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 953f95876be..c9c6cf1cb66 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -66,7 +66,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use core::cmp::Ordering; +use core::cmp::{self, Ordering}; use core::fmt; use core::hash::{self, Hash}; use core::intrinsics::{arith_offset, assume}; @@ -586,6 +586,31 @@ impl Vec { self.buf.shrink_to_fit(self.len); } + /// Shrinks the capacity of the vector with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// let mut vec = Vec::with_capacity(10); + /// vec.extend([1, 2, 3].iter().cloned()); + /// assert_eq!(vec.capacity(), 10); + /// vec.shrink_to(4); + /// assert!(vec.capacity() >= 4); + /// vec.shrink_to(0); + /// assert!(vec.capacity() >= 3); + /// ``` + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.buf.shrink_to_fit(cmp::max(self.len, min_capacity)); + } + /// Converts the vector into [`Box<[T]>`][owned slice]. /// /// Note that this will drop any excess capacity. diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 0658777f0a0..be6e8d0f22f 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -676,9 +676,42 @@ impl VecDeque { /// ``` #[stable(feature = "deque_extras_15", since = "1.5.0")] pub fn shrink_to_fit(&mut self) { + self.shrink_to(0); + } + + /// Shrinks the capacity of the `VecDeque` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::with_capacity(15); + /// buf.extend(0..4); + /// assert_eq!(buf.capacity(), 15); + /// buf.shrink_to(6); + /// assert!(buf.capacity() >= 6); + /// buf.shrink_to(0); + /// assert!(buf.capacity() >= 4); + /// ``` + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); + // +1 since the ringbuffer always leaves one space empty // len + 1 can't overflow for an existing, well-formed ringbuffer. - let target_cap = cmp::max(self.len() + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); + let target_cap = cmp::max( + cmp::max(min_capacity, self.len()) + 1, + MINIMUM_CAPACITY + 1 + ).next_power_of_two(); + if target_cap < self.cap() { // There are three cases of interest: // All elements are out of desired bounds diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index b18b38ec302..169d365c0ac 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -910,6 +910,46 @@ impl HashMap } } + /// Shrinks the capacity of the map with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::HashMap; + /// + /// let mut map: HashMap = HashMap::with_capacity(100); + /// map.insert(1, 2); + /// map.insert(3, 4); + /// assert!(map.capacity() >= 100); + /// map.shrink_to(10); + /// assert!(map.capacity() >= 10); + /// map.shrink_to(0); + /// assert!(map.capacity() >= 2); + /// ``` + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); + + let new_raw_cap = self.resize_policy.raw_capacity(max(self.len(), min_capacity)); + if self.raw_capacity() != new_raw_cap { + let old_table = replace(&mut self.table, RawTable::new(new_raw_cap)); + let old_size = old_table.size(); + + // Shrink the table. Naive algorithm for resizing: + for (h, k, v) in old_table.into_iter() { + self.insert_hashed_nocheck(h, k, v); + } + + debug_assert_eq!(self.table.size(), old_size); + } + } + /// Insert a pre-hashed key-value pair, without first checking /// that there's enough room in the buckets. Returns a reference to the /// newly insert value. diff --git a/src/libstd/collections/hash/set.rs b/src/libstd/collections/hash/set.rs index 9e63ba2717a..855563a5cb8 100644 --- a/src/libstd/collections/hash/set.rs +++ b/src/libstd/collections/hash/set.rs @@ -292,6 +292,34 @@ impl HashSet self.map.shrink_to_fit() } + /// Shrinks the capacity of the set with a lower limit. It will drop + /// down no lower than the supplied limit while maintaining the internal rules + /// and possibly leaving some space in accordance with the resize policy. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::HashSet; + /// + /// let mut set = HashSet::with_capacity(100); + /// set.insert(1); + /// set.insert(2); + /// assert!(set.capacity() >= 100); + /// set.shrink_to(10); + /// assert!(set.capacity() >= 10); + /// set.shrink_to(0); + /// assert!(set.capacity() >= 2); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.map.shrink_to(min_capacity) + } + /// An iterator visiting all elements in arbitrary order. /// The iterator element type is `&'a T`. /// diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 3959e8533be..7520121a8c2 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -295,6 +295,36 @@ impl OsString { self.inner.shrink_to_fit() } + /// Shrinks the capacity of the `OsString` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::ffi::OsString; + /// + /// let mut s = OsString::from("foo"); + /// + /// s.reserve(100); + /// assert!(s.capacity() >= 100); + /// + /// s.shrink_to(10); + /// assert!(s.capacity() >= 10); + /// s.shrink_to(0); + /// assert!(s.capacity() >= 3); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + /// Converts this `OsString` into a boxed [`OsStr`]. /// /// [`OsStr`]: struct.OsStr.html diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 0b06c5d4d65..edecf309d16 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -299,6 +299,7 @@ #![feature(raw)] #![feature(rustc_attrs)] #![feature(stdsimd)] +#![feature(shrink_to)] #![feature(slice_bytes)] #![feature(slice_concat_ext)] #![feature(slice_internals)] diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs index 655bfdb9167..da27787babb 100644 --- a/src/libstd/sys/redox/os_str.rs +++ b/src/libstd/sys/redox/os_str.rs @@ -104,6 +104,11 @@ impl Buf { self.inner.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + pub fn as_slice(&self) -> &Slice { unsafe { mem::transmute(&*self.inner) } } diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs index e0349387998..e43bc6da5f1 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys/unix/os_str.rs @@ -104,6 +104,11 @@ impl Buf { self.inner.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + pub fn as_slice(&self) -> &Slice { unsafe { mem::transmute(&*self.inner) } } diff --git a/src/libstd/sys/wasm/os_str.rs b/src/libstd/sys/wasm/os_str.rs index 543c22ebe18..84f560af69b 100644 --- a/src/libstd/sys/wasm/os_str.rs +++ b/src/libstd/sys/wasm/os_str.rs @@ -104,6 +104,11 @@ impl Buf { self.inner.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + pub fn as_slice(&self) -> &Slice { unsafe { mem::transmute(&*self.inner) } } diff --git a/src/libstd/sys/windows/os_str.rs b/src/libstd/sys/windows/os_str.rs index 414c9c5418e..bcc66b9954b 100644 --- a/src/libstd/sys/windows/os_str.rs +++ b/src/libstd/sys/windows/os_str.rs @@ -113,6 +113,11 @@ impl Buf { self.inner.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.inner.shrink_to(min_capacity) + } + #[inline] pub fn into_box(self) -> Box { unsafe { mem::transmute(self.inner.into_box()) } diff --git a/src/libstd/sys_common/wtf8.rs b/src/libstd/sys_common/wtf8.rs index 78b2bb5fe6e..dda4e1bab3b 100644 --- a/src/libstd/sys_common/wtf8.rs +++ b/src/libstd/sys_common/wtf8.rs @@ -253,6 +253,11 @@ impl Wtf8Buf { self.bytes.shrink_to_fit() } + #[inline] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.bytes.shrink_to(min_capacity) + } + /// Returns the number of bytes that this string buffer can hold without reallocating. #[inline] pub fn capacity(&self) -> usize { -- cgit 1.4.1-3-g733a5 From 554dd3e350721a7064932719ec925d1a1b5eb552 Mon Sep 17 00:00:00 2001 From: Alexis Hunt Date: Mon, 26 Mar 2018 21:18:50 -0400 Subject: Add missing '?' to format grammar. --- src/liballoc/fmt.rs | 15 ++++++++------- 1 file changed, 8 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index a092bfb3b0a..c69c2cd98c8 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -324,7 +324,7 @@ //! sign := '+' | '-' //! width := count //! precision := count | '*' -//! type := identifier | '' +//! type := identifier | '?' | '' //! count := parameter | integer //! parameter := argument '$' //! ``` @@ -514,17 +514,17 @@ pub use core::fmt::rt; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{Formatter, Result, Write}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{Octal, Binary}; +pub use core::fmt::{Binary, Octal}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{Display, Debug}; +pub use core::fmt::{Debug, Display}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{LowerHex, UpperHex, Pointer}; +pub use core::fmt::{LowerHex, Pointer, UpperHex}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{LowerExp, UpperExp}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::Error; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::fmt::{ArgumentV1, Arguments, write}; +pub use core::fmt::{write, ArgumentV1, Arguments}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; @@ -561,7 +561,8 @@ use string; pub fn format(args: Arguments) -> string::String { let capacity = args.estimated_capacity(); let mut output = string::String::with_capacity(capacity); - output.write_fmt(args) - .expect("a formatting trait implementation returned an error"); + output + .write_fmt(args) + .expect("a formatting trait implementation returned an error"); output } -- cgit 1.4.1-3-g733a5 From 1ef70a00ab2727360e36ec07bccc2838caa1db64 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 13 Mar 2018 22:29:59 +0100 Subject: Add repeat method on slice --- src/liballoc/slice.rs | 71 +++++++++++++++++++++++++++++++++++++++++++++++++++ src/liballoc/str.rs | 54 +-------------------------------------- 2 files changed, 72 insertions(+), 53 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index dc40062ef13..7085b7ea322 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1722,6 +1722,77 @@ impl [T] { // NB see hack module in this file hack::into_vec(self) } + + /// Creates a vector by repeating a slice `n` times. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(repeat_generic_slice)] + /// + /// fn main() { + /// assert_eq!([1, 2].repeat(3), vec![1, 2, 1, 2, 1, 2]); + /// } + /// ``` + #[unstable(feature = "repeat_generic_slice", + reason = "it's on str, why not on slice?", + issue = "48784")] + pub fn repeat(&self, n: usize) -> Vec where T: Copy { + if n == 0 { + return Vec::new(); + } + + // If `n` is larger than zero, it can be split as + // `n = 2^expn + rem (2^expn > rem, expn >= 0, rem >= 0)`. + // `2^expn` is the number represented by the leftmost '1' bit of `n`, + // and `rem` is the remaining part of `n`. + + // Using `Vec` to access `set_len()`. + let mut buf = Vec::with_capacity(self.len() * n); + + // `2^expn` repetition is done by doubling `buf` `expn`-times. + buf.extend(self); + { + let mut m = n >> 1; + // If `m > 0`, there are remaining bits up to the leftmost '1'. + while m > 0 { + // `buf.extend(buf)`: + unsafe { + ptr::copy_nonoverlapping( + buf.as_ptr(), + (buf.as_mut_ptr() as *mut T).add(buf.len()), + buf.len(), + ); + // `buf` has capacity of `self.len() * n`. + let buf_len = buf.len(); + buf.set_len(buf_len * 2); + } + + m >>= 1; + } + } + + // `rem` (`= n - 2^expn`) repetition is done by copying + // first `rem` repetitions from `buf` itself. + let rem_len = self.len() * n - buf.len(); // `self.len() * rem` + if rem_len > 0 { + // `buf.extend(buf[0 .. rem_len])`: + unsafe { + // This is non-overlapping since `2^expn > rem`. + ptr::copy_nonoverlapping( + buf.as_ptr(), + (buf.as_mut_ptr() as *mut T).add(buf.len()), + rem_len, + ); + // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). + let buf_cap = buf.capacity(); + buf.set_len(buf_cap); + } + } + buf + } } #[lang = "slice_u8"] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 14d5e96d2e7..2f118d094ec 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -2067,59 +2067,7 @@ impl str { /// ``` #[stable(feature = "repeat_str", since = "1.16.0")] pub fn repeat(&self, n: usize) -> String { - if n == 0 { - return String::new(); - } - - // If `n` is larger than zero, it can be split as - // `n = 2^expn + rem (2^expn > rem, expn >= 0, rem >= 0)`. - // `2^expn` is the number represented by the leftmost '1' bit of `n`, - // and `rem` is the remaining part of `n`. - - // Using `Vec` to access `set_len()`. - let mut buf = Vec::with_capacity(self.len() * n); - - // `2^expn` repetition is done by doubling `buf` `expn`-times. - buf.extend(self.as_bytes()); - { - let mut m = n >> 1; - // If `m > 0`, there are remaining bits up to the leftmost '1'. - while m > 0 { - // `buf.extend(buf)`: - unsafe { - ptr::copy_nonoverlapping( - buf.as_ptr(), - (buf.as_mut_ptr() as *mut u8).add(buf.len()), - buf.len(), - ); - // `buf` has capacity of `self.len() * n`. - let buf_len = buf.len(); - buf.set_len(buf_len * 2); - } - - m >>= 1; - } - } - - // `rem` (`= n - 2^expn`) repetition is done by copying - // first `rem` repetitions from `buf` itself. - let rem_len = self.len() * n - buf.len(); // `self.len() * rem` - if rem_len > 0 { - // `buf.extend(buf[0 .. rem_len])`: - unsafe { - // This is non-overlapping since `2^expn > rem`. - ptr::copy_nonoverlapping( - buf.as_ptr(), - (buf.as_mut_ptr() as *mut u8).add(buf.len()), - rem_len, - ); - // `buf.len() + rem_len` equals to `buf.capacity()` (`= self.len() * n`). - let buf_cap = buf.capacity(); - buf.set_len(buf_cap); - } - } - - unsafe { String::from_utf8_unchecked(buf) } + unsafe { String::from_utf8_unchecked(self.as_bytes().repeat(n)) } } /// Checks if all characters in this string are within the ASCII range. -- cgit 1.4.1-3-g733a5 From 3c1fea9c0de008d105e8e043b53c4aba57d0df65 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Tue, 27 Mar 2018 16:52:55 +0200 Subject: Add run-pass test for repeat-generic-slice feature --- src/liballoc/repeat-generic-slice.rs | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) create mode 100644 src/liballoc/repeat-generic-slice.rs (limited to 'src/liballoc') diff --git a/src/liballoc/repeat-generic-slice.rs b/src/liballoc/repeat-generic-slice.rs new file mode 100644 index 00000000000..5c14ee4fd83 --- /dev/null +++ b/src/liballoc/repeat-generic-slice.rs @@ -0,0 +1,19 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(repeat_generic_slice)] + +fn main() { + assert_eq!([1, 2].repeat(2), vec![1, 2, 1, 2]); + assert_eq!([1, 2, 3, 4].repeat(0), vec![]); + assert_eq!([1, 2, 3, 4].repeat(1), vec![1, 2, 3, 4]); + assert_eq!([1, 2, 3, 4].repeat(3), + vec![1, 2, 3, 4, 1, 2, 3, 4, 1, 2, 3, 4]); +} -- cgit 1.4.1-3-g733a5 From 0d15a3ee5402284450c312318871971f41603f80 Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Wed, 28 Mar 2018 14:10:18 +0200 Subject: Clarify "length" wording in `Vec::with_capacity`. --- src/liballoc/vec.rs | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 953f95876be..f8fc3f2bda1 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -334,9 +334,10 @@ impl Vec { /// The vector will be able to hold exactly `capacity` elements without /// reallocating. If `capacity` is 0, the vector will not allocate. /// - /// It is important to note that this function does not specify the *length* - /// of the returned vector, but only the *capacity*. For an explanation of - /// the difference between length and capacity, see *[Capacity and reallocation]*. + /// It is important to note that although the returned vector has the + /// *capacity* specified, the vector will have a zero *length*. For an + /// explanation of the difference between length and capacity, see + /// *[Capacity and reallocation]*. /// /// [Capacity and reallocation]: #capacity-and-reallocation /// -- cgit 1.4.1-3-g733a5 From 262be13643cf5e3ff4f4e880b2dee601d4740fd8 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 29 Mar 2018 09:34:39 +0900 Subject: Use f{32,64}::to_bits for is_zero test in vec::SpecFromElem vec::SpecFromElem provides an optimization to use calloc to fill a Vec when the element given to fill the Vec is represented by 0. For floats, the test for that currently used is `x == 0. && x.is_sign_positive()`. When compiled in a standalone function, rustc generates the following assembly: ``` xorps xmm1, xmm1 ucomisd xmm0, xmm1 setnp al sete cl and cl, al movq rax, xmm0 test rax, rax setns al and al, cl ret ``` A simpler test telling us whether the value is represented by 0, is `x.to_bits() == 0`, which rustc compiles to: ``` movq rax, xmm0 test rax, rax sete al ret ``` Not that the test is hot in any way, but it also makes it clearer what the intent in the rust code is. --- src/liballoc/vec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 953f95876be..3c2f91d08e3 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1574,8 +1574,8 @@ impl_spec_from_elem!(u64, |x| x == 0); impl_spec_from_elem!(u128, |x| x == 0); impl_spec_from_elem!(usize, |x| x == 0); -impl_spec_from_elem!(f32, |x: f32| x == 0. && x.is_sign_positive()); -impl_spec_from_elem!(f64, |x: f64| x == 0. && x.is_sign_positive()); +impl_spec_from_elem!(f32, |x: f32| x.to_bits() == 0); +impl_spec_from_elem!(f64, |x: f64| x.to_bits() == 0); //////////////////////////////////////////////////////////////////////////////// // Common trait implementations for Vec -- cgit 1.4.1-3-g733a5 From 6462c0bd7f03c9f1310ea9e4d259462e753e967e Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 29 Mar 2018 11:51:52 +0900 Subject: Remove unnecessary use core::hash in liballoc/boxed.rs It' only used for hash::Hasher, but Hasher is also imported. --- src/liballoc/boxed.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index bfd806f99e7..fdc3ef4efb8 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -62,7 +62,7 @@ use core::any::Any; use core::borrow; use core::cmp::Ordering; use core::fmt; -use core::hash::{self, Hash, Hasher}; +use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::{self, Unpin, Unsize}; use core::mem::{self, Pin}; @@ -508,7 +508,7 @@ impl Eq for Box {} #[stable(feature = "rust1", since = "1.0.0")] impl Hash for Box { - fn hash(&self, state: &mut H) { + fn hash(&self, state: &mut H) { (**self).hash(state); } } -- cgit 1.4.1-3-g733a5 From c3a63970dee2422e2fcc79d8b99303b4b046f444 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 19 Mar 2018 09:01:17 +0100 Subject: Move alloc::Bound to {core,std}::ops The stable reexport `std::collections::Bound` is now deprecated. Another deprecated reexport could be added in `alloc`, but that crate is unstable. --- src/liballoc/btree/map.rs | 4 +- src/liballoc/btree/set.rs | 2 +- src/liballoc/lib.rs | 51 ---------------------- src/liballoc/range.rs | 2 +- src/liballoc/string.rs | 2 +- src/liballoc/tests/btree/map.rs | 2 +- src/liballoc/vec.rs | 2 +- src/liballoc/vec_deque.rs | 2 +- src/libcore/ops/mod.rs | 2 +- src/libcore/ops/range.rs | 51 ++++++++++++++++++++++ src/librustc_data_structures/array_vec.rs | 2 +- src/libstd/collections/mod.rs | 3 +- .../sync-send-iterators-in-libcollections.rs | 2 +- 13 files changed, 64 insertions(+), 63 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index cada190032a..2ba56063e36 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -13,11 +13,11 @@ use core::fmt::Debug; use core::hash::{Hash, Hasher}; use core::iter::{FromIterator, Peekable, FusedIterator}; use core::marker::PhantomData; +use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::Index; use core::{fmt, intrinsics, mem, ptr}; use borrow::Borrow; -use Bound::{Excluded, Included, Unbounded}; use range::RangeArgument; use super::node::{self, Handle, NodeRef, marker}; @@ -804,7 +804,7 @@ impl BTreeMap { /// /// ``` /// use std::collections::BTreeMap; - /// use std::collections::Bound::Included; + /// use std::ops::Bound::Included; /// /// let mut map = BTreeMap::new(); /// map.insert(3, "a"); diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs index 2e3157147a0..d488dd6cbbd 100644 --- a/src/liballoc/btree/set.rs +++ b/src/liballoc/btree/set.rs @@ -240,7 +240,7 @@ impl BTreeSet { /// /// ``` /// use std::collections::BTreeSet; - /// use std::collections::Bound::Included; + /// use std::ops::Bound::Included; /// /// let mut set = BTreeSet::new(); /// set.insert(3); diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 19d64d8fea9..eddbd50ea03 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -204,57 +204,6 @@ mod std { pub use core::ops; // RangeFull } -/// An endpoint of a range of keys. -/// -/// # Examples -/// -/// `Bound`s are range endpoints: -/// -/// ``` -/// #![feature(collections_range)] -/// -/// use std::collections::range::RangeArgument; -/// use std::collections::Bound::*; -/// -/// assert_eq!((..100).start(), Unbounded); -/// assert_eq!((1..12).start(), Included(&1)); -/// assert_eq!((1..12).end(), Excluded(&12)); -/// ``` -/// -/// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`]. -/// Note that in most cases, it's better to use range syntax (`1..5`) instead. -/// -/// ``` -/// use std::collections::BTreeMap; -/// use std::collections::Bound::{Excluded, Included, Unbounded}; -/// -/// let mut map = BTreeMap::new(); -/// map.insert(3, "a"); -/// map.insert(5, "b"); -/// map.insert(8, "c"); -/// -/// for (key, value) in map.range((Excluded(3), Included(8))) { -/// println!("{}: {}", key, value); -/// } -/// -/// assert_eq!(Some((&3, &"a")), map.range((Unbounded, Included(5))).next()); -/// ``` -/// -/// [`BTreeMap::range`]: btree_map/struct.BTreeMap.html#method.range -#[stable(feature = "collections_bound", since = "1.17.0")] -#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] -pub enum Bound { - /// An inclusive bound. - #[stable(feature = "collections_bound", since = "1.17.0")] - Included(#[stable(feature = "collections_bound", since = "1.17.0")] T), - /// An exclusive bound. - #[stable(feature = "collections_bound", since = "1.17.0")] - Excluded(#[stable(feature = "collections_bound", since = "1.17.0")] T), - /// An infinite endpoint. Indicates that there is no bound in this direction. - #[stable(feature = "collections_bound", since = "1.17.0")] - Unbounded, -} - /// An intermediate trait for specialization of `Extend`. #[doc(hidden)] trait SpecExtend { diff --git a/src/liballoc/range.rs b/src/liballoc/range.rs index b03abc85180..7cadbf3c90a 100644 --- a/src/liballoc/range.rs +++ b/src/liballoc/range.rs @@ -15,7 +15,7 @@ //! Range syntax. use core::ops::{RangeFull, Range, RangeTo, RangeFrom, RangeInclusive, RangeToInclusive}; -use Bound::{self, Excluded, Included, Unbounded}; +use core::ops::Bound::{self, Excluded, Included, Unbounded}; /// `RangeArgument` is implemented by Rust's built-in range types, produced /// by range syntax like `..`, `a..`, `..b` or `c..d`. diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 23c12bef3aa..754c78f7779 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -59,6 +59,7 @@ use core::fmt; use core::hash; use core::iter::{FromIterator, FusedIterator}; +use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Add, AddAssign, Index, IndexMut}; use core::ptr; use core::str::pattern::Pattern; @@ -67,7 +68,6 @@ use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; use borrow::{Cow, ToOwned}; use range::RangeArgument; -use Bound::{Excluded, Included, Unbounded}; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; use vec::Vec; use boxed::Box; diff --git a/src/liballoc/tests/btree/map.rs b/src/liballoc/tests/btree/map.rs index 2393101040d..6ebdb86cc4a 100644 --- a/src/liballoc/tests/btree/map.rs +++ b/src/liballoc/tests/btree/map.rs @@ -9,8 +9,8 @@ // except according to those terms. use std::collections::BTreeMap; -use std::collections::Bound::{self, Excluded, Included, Unbounded}; use std::collections::btree_map::Entry::{Occupied, Vacant}; +use std::ops::Bound::{self, Excluded, Included, Unbounded}; use std::rc::Rc; use std::iter::FromIterator; diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index bcc999d7386..280570ecd65 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -75,6 +75,7 @@ use core::marker::PhantomData; use core::mem; #[cfg(not(test))] use core::num::Float; +use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{InPlace, Index, IndexMut, Place, Placer}; use core::ops; use core::ptr; @@ -87,7 +88,6 @@ use boxed::Box; use raw_vec::RawVec; use super::range::RangeArgument; use super::allocator::CollectionAllocErr; -use Bound::{Excluded, Included, Unbounded}; /// A contiguous growable array type, written `Vec` but pronounced 'vector'. /// diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index be6e8d0f22f..9efd730790d 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -21,6 +21,7 @@ use core::cmp::Ordering; use core::fmt; use core::iter::{repeat, FromIterator, FusedIterator}; use core::mem; +use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, Place, Placer, InPlace}; use core::ptr; use core::ptr::NonNull; @@ -33,7 +34,6 @@ use raw_vec::RawVec; use super::allocator::CollectionAllocErr; use super::range::RangeArgument; -use Bound::{Excluded, Included, Unbounded}; use super::vec::Vec; const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 234970a81fa..b0e75135282 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -192,7 +192,7 @@ pub use self::index::{Index, IndexMut}; pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[stable(feature = "inclusive_range", since = "1.26.0")] -pub use self::range::{RangeInclusive, RangeToInclusive}; +pub use self::range::{RangeInclusive, RangeToInclusive, Bound}; #[unstable(feature = "try_trait", issue = "42327")] pub use self::try::Try; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index be51f5239b0..dd44aedd09f 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -442,3 +442,54 @@ impl> RangeToInclusive { // RangeToInclusive cannot impl From> // because underflow would be possible with (..0).into() + +/// An endpoint of a range of keys. +/// +/// # Examples +/// +/// `Bound`s are range endpoints: +/// +/// ``` +/// #![feature(collections_range)] +/// +/// use std::collections::range::RangeArgument; +/// use std::ops::Bound::*; +/// +/// assert_eq!((..100).start(), Unbounded); +/// assert_eq!((1..12).start(), Included(&1)); +/// assert_eq!((1..12).end(), Excluded(&12)); +/// ``` +/// +/// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`]. +/// Note that in most cases, it's better to use range syntax (`1..5`) instead. +/// +/// ``` +/// use std::collections::BTreeMap; +/// use std::ops::Bound::{Excluded, Included, Unbounded}; +/// +/// let mut map = BTreeMap::new(); +/// map.insert(3, "a"); +/// map.insert(5, "b"); +/// map.insert(8, "c"); +/// +/// for (key, value) in map.range((Excluded(3), Included(8))) { +/// println!("{}: {}", key, value); +/// } +/// +/// assert_eq!(Some((&3, &"a")), map.range((Unbounded, Included(5))).next()); +/// ``` +/// +/// [`BTreeMap::range`]: ../../std/collections/btree_map/struct.BTreeMap.html#method.range +#[stable(feature = "collections_bound", since = "1.17.0")] +#[derive(Clone, Copy, Debug, Hash, PartialEq, Eq)] +pub enum Bound { + /// An inclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] + Included(#[stable(feature = "collections_bound", since = "1.17.0")] T), + /// An exclusive bound. + #[stable(feature = "collections_bound", since = "1.17.0")] + Excluded(#[stable(feature = "collections_bound", since = "1.17.0")] T), + /// An infinite endpoint. Indicates that there is no bound in this direction. + #[stable(feature = "collections_bound", since = "1.17.0")] + Unbounded, +} diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 511c407d45a..b40f2f92237 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -19,8 +19,8 @@ use std::slice; use std::fmt; use std::mem; use std::collections::range::RangeArgument; -use std::collections::Bound::{Excluded, Included, Unbounded}; use std::mem::ManuallyDrop; +use std::ops::Bound::{Excluded, Included, Unbounded}; pub unsafe trait Array { type Element; diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index be88f4e268a..e6f15a6119e 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -420,7 +420,8 @@ #![stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc::Bound; +#[rustc_deprecated(reason = "moved to `std::ops::Bound`", since = "1.26.0")] +pub use ops::Bound; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc::{BinaryHeap, BTreeMap, BTreeSet}; #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/test/run-pass/sync-send-iterators-in-libcollections.rs b/src/test/run-pass/sync-send-iterators-in-libcollections.rs index 903532e9bc8..e096fb3bbae 100644 --- a/src/test/run-pass/sync-send-iterators-in-libcollections.rs +++ b/src/test/run-pass/sync-send-iterators-in-libcollections.rs @@ -18,8 +18,8 @@ use std::collections::VecDeque; use std::collections::HashMap; use std::collections::HashSet; -use std::collections::Bound::Included; use std::mem; +use std::ops::Bound::Included; fn is_sync(_: T) where T: Sync {} fn is_send(_: T) where T: Send {} -- cgit 1.4.1-3-g733a5 From 16d3ba1b23195da2d53e058c58c2a41def914dec Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 19 Mar 2018 09:26:29 +0100 Subject: Move RangeArguments to {core::std}::ops and rename to RangeBounds These unstable items are deprecated: * The `std::collections::range::RangeArgument` reexport * The `std::collections::range` module. --- src/liballoc/btree/map.rs | 8 +- src/liballoc/btree/set.rs | 5 +- src/liballoc/lib.rs | 2 +- src/liballoc/range.rs | 152 ------------------------ src/liballoc/string.rs | 7 +- src/liballoc/vec.rs | 7 +- src/liballoc/vec_deque.rs | 5 +- src/libcore/ops/mod.rs | 2 +- src/libcore/ops/range.rs | 155 ++++++++++++++++++++++++- src/librustc_data_structures/accumulate_vec.rs | 5 +- src/librustc_data_structures/array_vec.rs | 4 +- src/librustc_data_structures/indexed_vec.rs | 7 +- src/libstd/collections/mod.rs | 8 +- 13 files changed, 183 insertions(+), 184 deletions(-) delete mode 100644 src/liballoc/range.rs (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 2ba56063e36..c604df7049e 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -15,10 +15,10 @@ use core::iter::{FromIterator, Peekable, FusedIterator}; use core::marker::PhantomData; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::Index; +use core::ops::RangeBounds; use core::{fmt, intrinsics, mem, ptr}; use borrow::Borrow; -use range::RangeArgument; use super::node::{self, Handle, NodeRef, marker}; use super::search; @@ -817,7 +817,7 @@ impl BTreeMap { /// ``` #[stable(feature = "btree_range", since = "1.17.0")] pub fn range(&self, range: R) -> Range - where T: Ord, K: Borrow, R: RangeArgument + where T: Ord, K: Borrow, R: RangeBounds { let root1 = self.root.as_ref(); let root2 = self.root.as_ref(); @@ -857,7 +857,7 @@ impl BTreeMap { /// ``` #[stable(feature = "btree_range", since = "1.17.0")] pub fn range_mut(&mut self, range: R) -> RangeMut - where T: Ord, K: Borrow, R: RangeArgument + where T: Ord, K: Borrow, R: RangeBounds { let root1 = self.root.as_mut(); let root2 = unsafe { ptr::read(&root1) }; @@ -1812,7 +1812,7 @@ fn last_leaf_edge } } -fn range_search>( +fn range_search>( root1: NodeRef, root2: NodeRef, range: R diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs index d488dd6cbbd..2aad476d315 100644 --- a/src/liballoc/btree/set.rs +++ b/src/liballoc/btree/set.rs @@ -16,12 +16,11 @@ use core::cmp::{min, max}; use core::fmt::Debug; use core::fmt; use core::iter::{Peekable, FromIterator, FusedIterator}; -use core::ops::{BitOr, BitAnd, BitXor, Sub}; +use core::ops::{BitOr, BitAnd, BitXor, Sub, RangeBounds}; use borrow::Borrow; use btree_map::{BTreeMap, Keys}; use super::Recover; -use range::RangeArgument; // FIXME(conventions): implement bounded iterators @@ -253,7 +252,7 @@ impl BTreeSet { /// ``` #[stable(feature = "btree_range", since = "1.17.0")] pub fn range(&self, range: R) -> Range - where K: Ord, T: Borrow, R: RangeArgument + where K: Ord, T: Borrow, R: RangeBounds { Range { iter: self.map.range(range) } } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index eddbd50ea03..e98b58994bf 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -88,6 +88,7 @@ #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] #![feature(coerce_unsized)] +#![feature(collections_range)] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(custom_attribute)] @@ -178,7 +179,6 @@ mod btree; pub mod borrow; pub mod fmt; pub mod linked_list; -pub mod range; pub mod slice; pub mod str; pub mod string; diff --git a/src/liballoc/range.rs b/src/liballoc/range.rs deleted file mode 100644 index 7cadbf3c90a..00000000000 --- a/src/liballoc/range.rs +++ /dev/null @@ -1,152 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "collections_range", - reason = "waiting for dust to settle on inclusive ranges", - issue = "30877")] - -//! Range syntax. - -use core::ops::{RangeFull, Range, RangeTo, RangeFrom, RangeInclusive, RangeToInclusive}; -use core::ops::Bound::{self, Excluded, Included, Unbounded}; - -/// `RangeArgument` is implemented by Rust's built-in range types, produced -/// by range syntax like `..`, `a..`, `..b` or `c..d`. -pub trait RangeArgument { - /// Start index bound. - /// - /// Returns the start value as a `Bound`. - /// - /// # Examples - /// - /// ``` - /// #![feature(alloc)] - /// #![feature(collections_range)] - /// - /// extern crate alloc; - /// - /// # fn main() { - /// use alloc::range::RangeArgument; - /// use alloc::Bound::*; - /// - /// assert_eq!((..10).start(), Unbounded); - /// assert_eq!((3..10).start(), Included(&3)); - /// # } - /// ``` - fn start(&self) -> Bound<&T>; - - /// End index bound. - /// - /// Returns the end value as a `Bound`. - /// - /// # Examples - /// - /// ``` - /// #![feature(alloc)] - /// #![feature(collections_range)] - /// - /// extern crate alloc; - /// - /// # fn main() { - /// use alloc::range::RangeArgument; - /// use alloc::Bound::*; - /// - /// assert_eq!((3..).end(), Unbounded); - /// assert_eq!((3..10).end(), Excluded(&10)); - /// # } - /// ``` - fn end(&self) -> Bound<&T>; -} - -// FIXME add inclusive ranges to RangeArgument - -impl RangeArgument for RangeFull { - fn start(&self) -> Bound<&T> { - Unbounded - } - fn end(&self) -> Bound<&T> { - Unbounded - } -} - -impl RangeArgument for RangeFrom { - fn start(&self) -> Bound<&T> { - Included(&self.start) - } - fn end(&self) -> Bound<&T> { - Unbounded - } -} - -impl RangeArgument for RangeTo { - fn start(&self) -> Bound<&T> { - Unbounded - } - fn end(&self) -> Bound<&T> { - Excluded(&self.end) - } -} - -impl RangeArgument for Range { - fn start(&self) -> Bound<&T> { - Included(&self.start) - } - fn end(&self) -> Bound<&T> { - Excluded(&self.end) - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl RangeArgument for RangeInclusive { - fn start(&self) -> Bound<&T> { - Included(&self.start) - } - fn end(&self) -> Bound<&T> { - Included(&self.end) - } -} - -#[stable(feature = "inclusive_range", since = "1.26.0")] -impl RangeArgument for RangeToInclusive { - fn start(&self) -> Bound<&T> { - Unbounded - } - fn end(&self) -> Bound<&T> { - Included(&self.end) - } -} - -impl RangeArgument for (Bound, Bound) { - fn start(&self) -> Bound<&T> { - match *self { - (Included(ref start), _) => Included(start), - (Excluded(ref start), _) => Excluded(start), - (Unbounded, _) => Unbounded, - } - } - - fn end(&self) -> Bound<&T> { - match *self { - (_, Included(ref end)) => Included(end), - (_, Excluded(ref end)) => Excluded(end), - (_, Unbounded) => Unbounded, - } - } -} - -impl<'a, T: ?Sized + 'a> RangeArgument for (Bound<&'a T>, Bound<&'a T>) { - fn start(&self) -> Bound<&T> { - self.0 - } - - fn end(&self) -> Bound<&T> { - self.1 - } -} diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 754c78f7779..aa202e23628 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -60,14 +60,13 @@ use core::fmt; use core::hash; use core::iter::{FromIterator, FusedIterator}; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{self, Add, AddAssign, Index, IndexMut}; +use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds}; use core::ptr; use core::str::pattern::Pattern; use std_unicode::lossy; use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; use borrow::{Cow, ToOwned}; -use range::RangeArgument; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; use vec::Vec; use boxed::Box; @@ -1484,7 +1483,7 @@ impl String { /// ``` #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self, range: R) -> Drain - where R: RangeArgument + where R: RangeBounds { // Memory safety // @@ -1548,7 +1547,7 @@ impl String { /// ``` #[unstable(feature = "splice", reason = "recently added", issue = "44643")] pub fn splice(&mut self, range: R, replace_with: &str) - where R: RangeArgument + where R: RangeBounds { // Memory safety // diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 280570ecd65..df08e46fe25 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -76,7 +76,7 @@ use core::mem; #[cfg(not(test))] use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{InPlace, Index, IndexMut, Place, Placer}; +use core::ops::{InPlace, Index, IndexMut, Place, Placer, RangeBounds}; use core::ops; use core::ptr; use core::ptr::NonNull; @@ -86,7 +86,6 @@ use borrow::ToOwned; use borrow::Cow; use boxed::Box; use raw_vec::RawVec; -use super::range::RangeArgument; use super::allocator::CollectionAllocErr; /// A contiguous growable array type, written `Vec` but pronounced 'vector'. @@ -1176,7 +1175,7 @@ impl Vec { /// ``` #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self, range: R) -> Drain - where R: RangeArgument + where R: RangeBounds { // Memory safety // @@ -1950,7 +1949,7 @@ impl Vec { #[inline] #[stable(feature = "vec_splice", since = "1.21.0")] pub fn splice(&mut self, range: R, replace_with: I) -> Splice - where R: RangeArgument, I: IntoIterator + where R: RangeBounds, I: IntoIterator { Splice { drain: self.drain(range), diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 9efd730790d..94d042a45aa 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -22,7 +22,7 @@ use core::fmt; use core::iter::{repeat, FromIterator, FusedIterator}; use core::mem; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Index, IndexMut, Place, Placer, InPlace}; +use core::ops::{Index, IndexMut, Place, Placer, InPlace, RangeBounds}; use core::ptr; use core::ptr::NonNull; use core::slice; @@ -33,7 +33,6 @@ use core::cmp; use raw_vec::RawVec; use super::allocator::CollectionAllocErr; -use super::range::RangeArgument; use super::vec::Vec; const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 @@ -969,7 +968,7 @@ impl VecDeque { #[inline] #[stable(feature = "drain", since = "1.6.0")] pub fn drain(&mut self, range: R) -> Drain - where R: RangeArgument + where R: RangeBounds { // Memory safety // diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index b0e75135282..0b480b618fc 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -192,7 +192,7 @@ pub use self::index::{Index, IndexMut}; pub use self::range::{Range, RangeFrom, RangeFull, RangeTo}; #[stable(feature = "inclusive_range", since = "1.26.0")] -pub use self::range::{RangeInclusive, RangeToInclusive, Bound}; +pub use self::range::{RangeInclusive, RangeToInclusive, RangeBounds, Bound}; #[unstable(feature = "try_trait", issue = "42327")] pub use self::try::Try; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index dd44aedd09f..b5aa81e36c4 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -452,8 +452,8 @@ impl> RangeToInclusive { /// ``` /// #![feature(collections_range)] /// -/// use std::collections::range::RangeArgument; /// use std::ops::Bound::*; +/// use std::ops::RangeBounds; /// /// assert_eq!((..100).start(), Unbounded); /// assert_eq!((1..12).start(), Included(&1)); @@ -493,3 +493,156 @@ pub enum Bound { #[stable(feature = "collections_bound", since = "1.17.0")] Unbounded, } + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +/// `RangeBounds` is implemented by Rust's built-in range types, produced +/// by range syntax like `..`, `a..`, `..b` or `c..d`. +pub trait RangeBounds { + /// Start index bound. + /// + /// Returns the start value as a `Bound`. + /// + /// # Examples + /// + /// ``` + /// #![feature(collections_range)] + /// + /// # fn main() { + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((..10).start(), Unbounded); + /// assert_eq!((3..10).start(), Included(&3)); + /// # } + /// ``` + fn start(&self) -> Bound<&T>; + + /// End index bound. + /// + /// Returns the end value as a `Bound`. + /// + /// # Examples + /// + /// ``` + /// #![feature(collections_range)] + /// + /// # fn main() { + /// use std::ops::Bound::*; + /// use std::ops::RangeBounds; + /// + /// assert_eq!((3..).end(), Unbounded); + /// assert_eq!((3..10).end(), Excluded(&10)); + /// # } + /// ``` + fn end(&self) -> Bound<&T>; +} + +use self::Bound::{Excluded, Included, Unbounded}; + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for RangeFull { + fn start(&self) -> Bound<&T> { + Unbounded + } + fn end(&self) -> Bound<&T> { + Unbounded + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for RangeFrom { + fn start(&self) -> Bound<&T> { + Included(&self.start) + } + fn end(&self) -> Bound<&T> { + Unbounded + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for RangeTo { + fn start(&self) -> Bound<&T> { + Unbounded + } + fn end(&self) -> Bound<&T> { + Excluded(&self.end) + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for Range { + fn start(&self) -> Bound<&T> { + Included(&self.start) + } + fn end(&self) -> Bound<&T> { + Excluded(&self.end) + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for RangeInclusive { + fn start(&self) -> Bound<&T> { + Included(&self.start) + } + fn end(&self) -> Bound<&T> { + Included(&self.end) + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for RangeToInclusive { + fn start(&self) -> Bound<&T> { + Unbounded + } + fn end(&self) -> Bound<&T> { + Included(&self.end) + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl RangeBounds for (Bound, Bound) { + fn start(&self) -> Bound<&T> { + match *self { + (Included(ref start), _) => Included(start), + (Excluded(ref start), _) => Excluded(start), + (Unbounded, _) => Unbounded, + } + } + + fn end(&self) -> Bound<&T> { + match *self { + (_, Included(ref end)) => Included(end), + (_, Excluded(ref end)) => Excluded(end), + (_, Unbounded) => Unbounded, + } + } +} + +#[unstable(feature = "collections_range", + reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", + issue = "30877")] +impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { + fn start(&self) -> Bound<&T> { + self.0 + } + + fn end(&self) -> Bound<&T> { + self.1 + } +} diff --git a/src/librustc_data_structures/accumulate_vec.rs b/src/librustc_data_structures/accumulate_vec.rs index 52306de74cb..f50b8cadf15 100644 --- a/src/librustc_data_structures/accumulate_vec.rs +++ b/src/librustc_data_structures/accumulate_vec.rs @@ -15,11 +15,10 @@ //! //! The N above is determined by Array's implementor, by way of an associated constant. -use std::ops::{Deref, DerefMut}; +use std::ops::{Deref, DerefMut, RangeBounds}; use std::iter::{self, IntoIterator, FromIterator}; use std::slice; use std::vec; -use std::collections::range::RangeArgument; use rustc_serialize::{Encodable, Encoder, Decodable, Decoder}; @@ -74,7 +73,7 @@ impl AccumulateVec { } pub fn drain(&mut self, range: R) -> Drain - where R: RangeArgument + where R: RangeBounds { match *self { AccumulateVec::Array(ref mut v) => { diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index b40f2f92237..db1cfb5c767 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -18,9 +18,9 @@ use std::hash::{Hash, Hasher}; use std::slice; use std::fmt; use std::mem; -use std::collections::range::RangeArgument; use std::mem::ManuallyDrop; use std::ops::Bound::{Excluded, Included, Unbounded}; +use std::ops::RangeBounds; pub unsafe trait Array { type Element; @@ -106,7 +106,7 @@ impl ArrayVec { } pub fn drain(&mut self, range: R) -> Drain - where R: RangeArgument + where R: RangeBounds { // Memory safety // diff --git a/src/librustc_data_structures/indexed_vec.rs b/src/librustc_data_structures/indexed_vec.rs index cbb3ff51715..1fb63afc72f 100644 --- a/src/librustc_data_structures/indexed_vec.rs +++ b/src/librustc_data_structures/indexed_vec.rs @@ -8,12 +8,11 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use std::collections::range::RangeArgument; use std::fmt::Debug; use std::iter::{self, FromIterator}; use std::slice; use std::marker::PhantomData; -use std::ops::{Index, IndexMut, Range}; +use std::ops::{Index, IndexMut, Range, RangeBounds}; use std::fmt; use std::vec; use std::u32; @@ -448,13 +447,13 @@ impl IndexVec { } #[inline] - pub fn drain<'a, R: RangeArgument>( + pub fn drain<'a, R: RangeBounds>( &'a mut self, range: R) -> impl Iterator + 'a { self.raw.drain(range) } #[inline] - pub fn drain_enumerated<'a, R: RangeArgument>( + pub fn drain_enumerated<'a, R: RangeBounds>( &'a mut self, range: R) -> impl Iterator + 'a { self.raw.drain(range).enumerate().map(IntoIdx { _marker: PhantomData }) } diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index e6f15a6119e..47ea3bc26bf 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -436,8 +436,12 @@ pub use self::hash_map::HashMap; #[stable(feature = "rust1", since = "1.0.0")] pub use self::hash_set::HashSet; -#[stable(feature = "rust1", since = "1.0.0")] -pub use alloc::range; +#[unstable(feature = "collections_range", issue = "30877")] +#[rustc_deprecated(reason = "renamed and moved to `std::ops::RangeBounds`", since = "1.26.0")] +/// Range syntax +pub mod range { + pub use ops::RangeBounds as RangeArgument; +} #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] pub use alloc::allocator::CollectionAllocErr; -- cgit 1.4.1-3-g733a5 From 94d1970bba87f2d2893f6e934e4c3f02ed50604d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 28 Mar 2018 22:37:37 +0200 Subject: Move the alloc::allocator module to core::heap This is the `Alloc` trait and its dependencies. --- src/liballoc/allocator.rs | 1082 --------------------------------------------- src/liballoc/heap.rs | 2 +- src/liballoc/lib.rs | 7 +- src/libcore/heap.rs | 1082 +++++++++++++++++++++++++++++++++++++++++++++ src/libcore/lib.rs | 4 + src/libstd/heap.rs | 3 +- 6 files changed, 1093 insertions(+), 1087 deletions(-) delete mode 100644 src/liballoc/allocator.rs create mode 100644 src/libcore/heap.rs (limited to 'src/liballoc') diff --git a/src/liballoc/allocator.rs b/src/liballoc/allocator.rs deleted file mode 100644 index fdc4efc66b9..00000000000 --- a/src/liballoc/allocator.rs +++ /dev/null @@ -1,1082 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] - -use core::cmp; -use core::fmt; -use core::mem; -use core::usize; -use core::ptr::{self, NonNull}; - -/// Represents the combination of a starting address and -/// a total capacity of the returned block. -#[derive(Debug)] -pub struct Excess(pub *mut u8, pub usize); - -fn size_align() -> (usize, usize) { - (mem::size_of::(), mem::align_of::()) -} - -/// Layout of a block of memory. -/// -/// An instance of `Layout` describes a particular layout of memory. -/// You build a `Layout` up as an input to give to an allocator. -/// -/// All layouts have an associated non-negative size and a -/// power-of-two alignment. -/// -/// (Note however that layouts are *not* required to have positive -/// size, even though many allocators require that all memory -/// requests have positive size. A caller to the `Alloc::alloc` -/// method must either ensure that conditions like this are met, or -/// use specific allocators with looser requirements.) -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Layout { - // size of the requested block of memory, measured in bytes. - size: usize, - - // alignment of the requested block of memory, measured in bytes. - // we ensure that this is always a power-of-two, because API's - // like `posix_memalign` require it and it is a reasonable - // constraint to impose on Layout constructors. - // - // (However, we do not analogously require `align >= sizeof(void*)`, - // even though that is *also* a requirement of `posix_memalign`.) - align: usize, -} - - -// FIXME: audit default implementations for overflow errors, -// (potentially switching to overflowing_add and -// overflowing_mul as necessary). - -impl Layout { - /// Constructs a `Layout` from a given `size` and `align`, - /// or returns `None` if any of the following conditions - /// are not met: - /// - /// * `align` must be a power of two, - /// - /// * `align` must not exceed 231 (i.e. `1 << 31`), - /// - /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow (i.e. the rounded value must be less than - /// `usize::MAX`). - #[inline] - pub fn from_size_align(size: usize, align: usize) -> Option { - if !align.is_power_of_two() { - return None; - } - - if align > (1 << 31) { - return None; - } - - // (power-of-two implies align != 0.) - - // Rounded up size is: - // size_rounded_up = (size + align - 1) & !(align - 1); - // - // We know from above that align != 0. If adding (align - 1) - // does not overflow, then rounding up will be fine. - // - // Conversely, &-masking with !(align - 1) will subtract off - // only low-order-bits. Thus if overflow occurs with the sum, - // the &-mask cannot subtract enough to undo that overflow. - // - // Above implies that checking for summation overflow is both - // necessary and sufficient. - if size > usize::MAX - (align - 1) { - return None; - } - - unsafe { - Some(Layout::from_size_align_unchecked(size, align)) - } - } - - /// Creates a layout, bypassing all checks. - /// - /// # Safety - /// - /// This function is unsafe as it does not verify that `align` is - /// a power-of-two that is also less than or equal to 231, nor - /// that `size` aligned to `align` fits within the address space - /// (i.e. the `Layout::from_size_align` preconditions). - #[inline] - pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { - Layout { size: size, align: align } - } - - /// The minimum size in bytes for a memory block of this layout. - #[inline] - pub fn size(&self) -> usize { self.size } - - /// The minimum byte alignment for a memory block of this layout. - #[inline] - pub fn align(&self) -> usize { self.align } - - /// Constructs a `Layout` suitable for holding a value of type `T`. - pub fn new() -> Self { - let (size, align) = size_align::(); - Layout::from_size_align(size, align).unwrap() - } - - /// Produces layout describing a record that could be used to - /// allocate backing structure for `T` (which could be a trait - /// or other unsized type like a slice). - pub fn for_value(t: &T) -> Self { - let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); - Layout::from_size_align(size, align).unwrap() - } - - /// Creates a layout describing the record that can hold a value - /// of the same layout as `self`, but that also is aligned to - /// alignment `align` (measured in bytes). - /// - /// If `self` already meets the prescribed alignment, then returns - /// `self`. - /// - /// Note that this method does not add any padding to the overall - /// size, regardless of whether the returned layout has a different - /// alignment. In other words, if `K` has size 16, `K.align_to(32)` - /// will *still* have size 16. - /// - /// # Panics - /// - /// Panics if the combination of `self.size` and the given `align` - /// violates the conditions listed in `from_size_align`. - #[inline] - pub fn align_to(&self, align: usize) -> Self { - Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap() - } - - /// Returns the amount of padding we must insert after `self` - /// to ensure that the following address will satisfy `align` - /// (measured in bytes). - /// - /// E.g. if `self.size` is 9, then `self.padding_needed_for(4)` - /// returns 3, because that is the minimum number of bytes of - /// padding required to get a 4-aligned address (assuming that the - /// corresponding memory block starts at a 4-aligned address). - /// - /// The return value of this function has no meaning if `align` is - /// not a power-of-two. - /// - /// Note that the utility of the returned value requires `align` - /// to be less than or equal to the alignment of the starting - /// address for the whole allocated block of memory. One way to - /// satisfy this constraint is to ensure `align <= self.align`. - #[inline] - pub fn padding_needed_for(&self, align: usize) -> usize { - let len = self.size(); - - // Rounded up value is: - // len_rounded_up = (len + align - 1) & !(align - 1); - // and then we return the padding difference: `len_rounded_up - len`. - // - // We use modular arithmetic throughout: - // - // 1. align is guaranteed to be > 0, so align - 1 is always - // valid. - // - // 2. `len + align - 1` can overflow by at most `align - 1`, - // so the &-mask wth `!(align - 1)` will ensure that in the - // case of overflow, `len_rounded_up` will itself be 0. - // Thus the returned padding, when added to `len`, yields 0, - // which trivially satisfies the alignment `align`. - // - // (Of course, attempts to allocate blocks of memory whose - // size and padding overflow in the above manner should cause - // the allocator to yield an error anyway.) - - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - return len_rounded_up.wrapping_sub(len); - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with a suitable amount of padding between each to - /// ensure that each instance is given its requested size and - /// alignment. On success, returns `(k, offs)` where `k` is the - /// layout of the array and `offs` is the distance between the start - /// of each element in the array. - /// - /// On arithmetic overflow, returns `None`. - #[inline] - pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { - let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?; - let alloc_size = padded_size.checked_mul(n)?; - - // We can assume that `self.align` is a power-of-two that does - // not exceed 231. Furthermore, `alloc_size` has already been - // rounded up to a multiple of `self.align`; therefore, the - // call to `Layout::from_size_align` below should never panic. - Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) - } - - /// Creates a layout describing the record for `self` followed by - /// `next`, including any necessary padding to ensure that `next` - /// will be properly aligned. Note that the result layout will - /// satisfy the alignment properties of both `self` and `next`. - /// - /// Returns `Some((k, offset))`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// On arithmetic overflow, returns `None`. - pub fn extend(&self, next: Self) -> Option<(Self, usize)> { - let new_align = cmp::max(self.align, next.align); - let realigned = Layout::from_size_align(self.size, new_align)?; - - let pad = realigned.padding_needed_for(next.align); - - let offset = self.size.checked_add(pad)?; - let new_size = offset.checked_add(next.size)?; - - let layout = Layout::from_size_align(new_size, new_align)?; - Some((layout, offset)) - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with no padding between each instance. - /// - /// Note that, unlike `repeat`, `repeat_packed` does not guarantee - /// that the repeated instances of `self` will be properly - /// aligned, even if a given instance of `self` is properly - /// aligned. In other words, if the layout returned by - /// `repeat_packed` is used to allocate an array, it is not - /// guaranteed that all elements in the array will be properly - /// aligned. - /// - /// On arithmetic overflow, returns `None`. - pub fn repeat_packed(&self, n: usize) -> Option { - let size = self.size().checked_mul(n)?; - Layout::from_size_align(size, self.align) - } - - /// Creates a layout describing the record for `self` followed by - /// `next` with no additional padding between the two. Since no - /// padding is inserted, the alignment of `next` is irrelevant, - /// and is not incorporated *at all* into the resulting layout. - /// - /// Returns `(k, offset)`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// (The `offset` is always the same as `self.size()`; we use this - /// signature out of convenience in matching the signature of - /// `extend`.) - /// - /// On arithmetic overflow, returns `None`. - pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { - let new_size = self.size().checked_add(next.size())?; - let layout = Layout::from_size_align(new_size, self.align)?; - Some((layout, self.size())) - } - - /// Creates a layout describing the record for a `[T; n]`. - /// - /// On arithmetic overflow, returns `None`. - pub fn array(n: usize) -> Option { - Layout::new::() - .repeat(n) - .map(|(k, offs)| { - debug_assert!(offs == mem::size_of::()); - k - }) - } -} - -/// The `AllocErr` error specifies whether an allocation failure is -/// specifically due to resource exhaustion or if it is due to -/// something wrong when combining the given input arguments with this -/// allocator. -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum AllocErr { - /// Error due to hitting some resource limit or otherwise running - /// out of memory. This condition strongly implies that *some* - /// series of deallocations would allow a subsequent reissuing of - /// the original allocation request to succeed. - Exhausted { request: Layout }, - - /// Error due to allocator being fundamentally incapable of - /// satisfying the original request. This condition implies that - /// such an allocation request will never succeed on the given - /// allocator, regardless of environment, memory pressure, or - /// other contextual conditions. - /// - /// For example, an allocator that does not support requests for - /// large memory blocks might return this error variant. - Unsupported { details: &'static str }, -} - -impl AllocErr { - #[inline] - pub fn invalid_input(details: &'static str) -> Self { - AllocErr::Unsupported { details: details } - } - #[inline] - pub fn is_memory_exhausted(&self) -> bool { - if let AllocErr::Exhausted { .. } = *self { true } else { false } - } - #[inline] - pub fn is_request_unsupported(&self) -> bool { - if let AllocErr::Unsupported { .. } = *self { true } else { false } - } - #[inline] - pub fn description(&self) -> &str { - match *self { - AllocErr::Exhausted { .. } => "allocator memory exhausted", - AllocErr::Unsupported { .. } => "unsupported allocator request", - } - } -} - -// (we need this for downstream impl of trait Error) -impl fmt::Display for AllocErr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// The `CannotReallocInPlace` error is used when `grow_in_place` or -/// `shrink_in_place` were unable to reuse the given memory block for -/// a requested layout. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct CannotReallocInPlace; - -impl CannotReallocInPlace { - pub fn description(&self) -> &str { - "cannot reallocate allocator's memory in place" - } -} - -// (we need this for downstream impl of trait Error) -impl fmt::Display for CannotReallocInPlace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// Augments `AllocErr` with a CapacityOverflow variant. -#[derive(Clone, PartialEq, Eq, Debug)] -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -pub enum CollectionAllocErr { - /// Error due to the computed capacity exceeding the collection's maximum - /// (usually `isize::MAX` bytes). - CapacityOverflow, - /// Error due to the allocator (see the `AllocErr` type's docs). - AllocErr(AllocErr), -} - -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -impl From for CollectionAllocErr { - fn from(err: AllocErr) -> Self { - CollectionAllocErr::AllocErr(err) - } -} - -/// An implementation of `Alloc` can allocate, reallocate, and -/// deallocate arbitrary blocks of data described via `Layout`. -/// -/// Some of the methods require that a memory block be *currently -/// allocated* via an allocator. This means that: -/// -/// * the starting address for that memory block was previously -/// returned by a previous call to an allocation method (`alloc`, -/// `alloc_zeroed`, `alloc_excess`, `alloc_one`, `alloc_array`) or -/// reallocation method (`realloc`, `realloc_excess`, or -/// `realloc_array`), and -/// -/// * the memory block has not been subsequently deallocated, where -/// blocks are deallocated either by being passed to a deallocation -/// method (`dealloc`, `dealloc_one`, `dealloc_array`) or by being -/// passed to a reallocation method (see above) that returns `Ok`. -/// -/// A note regarding zero-sized types and zero-sized layouts: many -/// methods in the `Alloc` trait state that allocation requests -/// must be non-zero size, or else undefined behavior can result. -/// -/// * However, some higher-level allocation methods (`alloc_one`, -/// `alloc_array`) are well-defined on zero-sized types and can -/// optionally support them: it is left up to the implementor -/// whether to return `Err`, or to return `Ok` with some pointer. -/// -/// * If an `Alloc` implementation chooses to return `Ok` in this -/// case (i.e. the pointer denotes a zero-sized inaccessible block) -/// then that returned pointer must be considered "currently -/// allocated". On such an allocator, *all* methods that take -/// currently-allocated pointers as inputs must accept these -/// zero-sized pointers, *without* causing undefined behavior. -/// -/// * In other words, if a zero-sized pointer can flow out of an -/// allocator, then that allocator must likewise accept that pointer -/// flowing back into its deallocation and reallocation methods. -/// -/// Some of the methods require that a layout *fit* a memory block. -/// What it means for a layout to "fit" a memory block means (or -/// equivalently, for a memory block to "fit" a layout) is that the -/// following two conditions must hold: -/// -/// 1. The block's starting address must be aligned to `layout.align()`. -/// -/// 2. The block's size must fall in the range `[use_min, use_max]`, where: -/// -/// * `use_min` is `self.usable_size(layout).0`, and -/// -/// * `use_max` is the capacity that was (or would have been) -/// returned when (if) the block was allocated via a call to -/// `alloc_excess` or `realloc_excess`. -/// -/// Note that: -/// -/// * the size of the layout most recently used to allocate the block -/// is guaranteed to be in the range `[use_min, use_max]`, and -/// -/// * a lower-bound on `use_max` can be safely approximated by a call to -/// `usable_size`. -/// -/// * 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 - // behavior in response to a zero size allocation request ; - // e.g. in C, `malloc` of 0 will either return a null pointer or a - // unique pointer, but will not have arbitrary undefined - // behavior. Rust should consider revising the alloc::heap crate - // to reflect this reality.) - - /// Returns a pointer meeting the size and alignment guarantees of - /// `layout`. - /// - /// If this method returns an `Ok(addr)`, then the `addr` returned - /// will be non-null address pointing to a block of storage - /// suitable for holding an instance of `layout`. - /// - /// The returned block of storage may or may not have its contents - /// initialized. (Extension subtraits might restrict this - /// behavior, e.g. to ensure initialization to particular sets of - /// bit patterns.) - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure that `layout` has non-zero size. - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g. guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints. - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; - - /// Deallocate the memory referenced by `ptr`. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must denote a block of memory currently allocated via - /// this allocator, - /// - /// * `layout` must *fit* that block of memory, - /// - /// * In addition to fitting the block of memory `layout`, the - /// alignment of the `layout` must match the alignment used - /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); - - /// Allocator-specific method for signaling an out-of-memory - /// condition. - /// - /// `oom` aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request (signaled by an error such as - /// `AllocErr::Exhausted`), and wish to abandon computation rather - /// than attempt to recover locally. Such clients should pass the - /// signaling error value back into `oom`, where the allocator - /// may incorporate that error value into its diagnostic report - /// before aborting. - /// - /// Implementations of the `oom` method are discouraged from - /// infinitely regressing in nested calls to `oom`. In - /// practice this means implementors should eschew allocating, - /// especially from `self` (directly or indirectly). - /// - /// Implementations of the allocation and reallocation methods - /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from - /// panicking (or aborting) in the event of memory exhaustion; - /// instead they should return an appropriate error from the - /// invoked method, and let the client decide whether to invoke - /// this `oom` method in response. - fn oom(&mut self, _: AllocErr) -> ! { - unsafe { ::core::intrinsics::abort() } - } - - // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == - // usable_size - - /// Returns bounds on the guaranteed usable size of a successful - /// allocation created with the specified `layout`. - /// - /// In particular, if one has a memory block allocated via a given - /// allocator `a` and layout `k` where `a.usable_size(k)` returns - /// `(l, u)`, then one can pass that block to `a.dealloc()` with a - /// layout in the size range [l, u]. - /// - /// (All implementors of `usable_size` must ensure that - /// `l <= k.size() <= u`) - /// - /// Both the lower- and upper-bounds (`l` and `u` respectively) - /// are provided, because an allocator based on size classes could - /// misbehave if one attempts to deallocate a block without - /// providing a correct value for its size (i.e., one within the - /// range `[l, u]`). - /// - /// Clients who wish to make use of excess capacity are encouraged - /// to use the `alloc_excess` and `realloc_excess` instead, as - /// this method is constrained to report conservative values that - /// serve as valid bounds for *all possible* allocation method - /// calls. - /// - /// However, for clients that do not wish to track the capacity - /// returned by `alloc_excess` locally, this method is likely to - /// produce useful results. - #[inline] - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - (layout.size(), layout.size()) - } - - // == METHODS FOR MEMORY REUSE == - // realloc. alloc_excess, realloc_excess - - /// Returns a pointer suitable for holding data described by - /// `new_layout`, meeting its size and alignment guarantees. To - /// accomplish this, this may extend or shrink the allocation - /// referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then ownership of the memory block - /// referenced by `ptr` has been transferred to this - /// allocator. The memory may or may not have been freed, and - /// should be considered unusable (unless of course it was - /// transferred back to the caller again via the return value of - /// this method). - /// - /// If this method returns `Err`, then ownership of the memory - /// block has not been transferred to this allocator, and the - /// contents of the memory block are unaltered. - /// - /// For best results, `new_layout` should not impose a different - /// alignment constraint than `layout`. (In other words, - /// `new_layout.align()` should equal `layout.align()`.) However, - /// behavior is well-defined (though underspecified) when this - /// constraint is violated; further discussion below. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` - /// argument need not fit it.) - /// - /// * `new_layout` must have size greater than zero. - /// - /// * the alignment of `new_layout` is non-zero. - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g. guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returns `Err` only if `new_layout` does not match the - /// alignment of `layout`, or does not meet the allocator's size - /// and alignment constraints of the allocator, or if reallocation - /// otherwise fails. - /// - /// (Note the previous sentence did not say "if and only if" -- in - /// particular, an implementation of this method *can* return `Ok` - /// if `new_layout.align() != old_layout.align()`; or it can - /// return `Err` in that scenario, depending on whether this - /// allocator can dynamically adjust the alignment constraint for - /// the block.) - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - let new_size = new_layout.size(); - let old_size = layout.size(); - let aligns_match = layout.align == new_layout.align; - - if new_size >= old_size && aligns_match { - if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { - return Ok(ptr); - } - } else if new_size < old_size && aligns_match { - if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { - return Ok(ptr); - } - } - - // otherwise, fall back on alloc + copy + dealloc. - let result = self.alloc(new_layout); - if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); - self.dealloc(ptr, layout); - } - result - } - - /// Behaves like `alloc`, but also ensures that the contents - /// are set to zero before being returned. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `alloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let size = layout.size(); - let p = self.alloc(layout); - if let Ok(p) = p { - ptr::write_bytes(p, 0, size); - } - p - } - - /// Behaves like `alloc`, but also returns the whole size of - /// the returned block. For some `layout` inputs, like arrays, this - /// may include extra storage usable for additional data. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `alloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let usable_size = self.usable_size(&layout); - self.alloc(layout).map(|p| Excess(p, usable_size.1)) - } - - /// Behaves like `realloc`, but also returns the whole size of - /// the returned block. For some `layout` inputs, like arrays, this - /// may include extra storage usable for additional data. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `realloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `realloc`. - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let usable_size = self.usable_size(&new_layout); - self.realloc(ptr, layout, new_layout) - .map(|p| Excess(p, usable_size.1)) - } - - /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and thus can - /// be used to carry data of that layout. (The allocator is allowed to - /// expend effort to accomplish this, such as extending the memory block to - /// include successor blocks, or virtual memory tricks.) - /// - /// Regardless of what this method returns, ownership of the - /// memory block referenced by `ptr` has not been transferred, and - /// the contents of the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, - /// - /// * `new_layout.size()` must not be less than `layout.size()`, - /// - /// * `new_layout.align()` must equal `layout.align()`. - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from - /// `grow_in_place` failures without aborting, or to fall back on - /// another reallocation method before resorting to an abort. - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size >= layout.size); - debug_assert!(new_layout.align == layout.align); - let (_l, u) = self.usable_size(&layout); - // _l <= layout.size() [guaranteed by usable_size()] - // layout.size() <= new_layout.size() [required by this method] - if new_layout.size <= u { - return Ok(()); - } else { - return Err(CannotReallocInPlace); - } - } - - /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and - /// thus can only be used to carry data of that smaller - /// layout. (The allocator is allowed to take advantage of this, - /// carving off portions of the block for reuse elsewhere.) The - /// truncated contents of the block within the smaller layout are - /// unaltered, and ownership of block has not been transferred. - /// - /// If this returns `Err`, then the memory block is considered to - /// still represent the original (larger) `layout`. None of the - /// block has been carved off for reuse elsewhere, ownership of - /// the memory block has not been transferred, and the contents of - /// the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, - /// - /// * `new_layout.size()` must not be greater than `layout.size()` - /// (and must be greater than zero), - /// - /// * `new_layout.align()` must equal `layout.align()`. - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from - /// `shrink_in_place` failures without aborting, or to fall back - /// on another reallocation method before resorting to an abort. - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size <= layout.size); - debug_assert!(new_layout.align == layout.align); - let (l, _u) = self.usable_size(&layout); - // layout.size() <= _u [guaranteed by usable_size()] - // new_layout.size() <= layout.size() [required by this method] - if l <= new_layout.size { - return Ok(()); - } else { - return Err(CannotReallocInPlace); - } - } - - - // == COMMON USAGE PATTERNS == - // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array - - /// Allocates a block suitable for holding an instance of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` - /// must be considered "currently allocated" and must be - /// acceptable input to methods such as `realloc` or `dealloc`, - /// *even if* `T` is a zero-sized type. In other words, if your - /// `Alloc` implementation overrides this method in a manner - /// that can return a zero-sized `ptr`, then all reallocation and - /// deallocation methods need to be similarly overridden to accept - /// such values as input. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `T` does not meet allocator's size or alignment constraints. - /// - /// For zero-sized `T`, may return either of `Ok` or `Err`, but - /// will *not* yield undefined behavior. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - fn alloc_one(&mut self) -> Result, AllocErr> - where Self: Sized - { - let k = Layout::new::(); - if k.size() > 0 { - unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } - } else { - Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) - } - } - - /// Deallocates a block suitable for holding an instance of `T`. - /// - /// The given block must have been produced by this allocator, - /// and must be suitable for storing a `T` (in terms of alignment - /// as well as minimum and maximum size); otherwise yields - /// undefined behavior. - /// - /// Captures a common usage pattern for allocators. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure both: - /// - /// * `ptr` must denote a block of memory currently allocated via this allocator - /// - /// * the layout of `T` must *fit* that block of memory. - unsafe fn dealloc_one(&mut self, ptr: NonNull) - where Self: Sized - { - let raw_ptr = ptr.as_ptr() as *mut u8; - let k = Layout::new::(); - if k.size() > 0 { - self.dealloc(raw_ptr, k); - } - } - - /// Allocates a block suitable for holding `n` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` - /// must be considered "currently allocated" and must be - /// acceptable input to methods such as `realloc` or `dealloc`, - /// *even if* `T` is a zero-sized type. In other words, if your - /// `Alloc` implementation overrides this method in a manner - /// that can return a zero-sized `ptr`, then all reallocation and - /// deallocation methods need to be similarly overridden to accept - /// such values as input. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `[T; n]` does not meet allocator's size or alignment - /// constraints. - /// - /// For zero-sized `T` or `n == 0`, may return either of `Ok` or - /// `Err`, but will *not* yield undefined behavior. - /// - /// Always returns `Err` on arithmetic overflow. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - fn alloc_array(&mut self, n: usize) -> Result, AllocErr> - where Self: Sized - { - match Layout::array::(n) { - Some(ref layout) if layout.size() > 0 => { - unsafe { - self.alloc(layout.clone()) - .map(|p| { - NonNull::new_unchecked(p as *mut T) - }) - } - } - _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), - } - } - - /// Reallocates a block previously suitable for holding `n_old` - /// instances of `T`, returning a block suitable for holding - /// `n_new` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * the layout of `[T; n_old]` must *fit* that block of memory. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `[T; n_new]` does not meet allocator's size or alignment - /// constraints. - /// - /// For zero-sized `T` or `n_new == 0`, may return either of `Ok` or - /// `Err`, but will *not* yield undefined behavior. - /// - /// Always returns `Err` on arithmetic overflow. - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc_array(&mut self, - ptr: NonNull, - n_old: usize, - n_new: usize) -> Result, AllocErr> - where Self: Sized - { - match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { - (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { - self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) - .map(|p| NonNull::new_unchecked(p as *mut T)) - } - _ => { - Err(AllocErr::invalid_input("invalid layout for realloc_array")) - } - } - } - - /// Deallocates a block suitable for holding `n` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure both: - /// - /// * `ptr` must denote a block of memory currently allocated via this allocator - /// - /// * the layout of `[T; n]` must *fit* that block of memory. - /// - /// # Errors - /// - /// Returning `Err` indicates that either `[T; n]` or the given - /// memory block does not meet allocator's size or alignment - /// constraints. - /// - /// Always returns `Err` on arithmetic overflow. - unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> - where Self: Sized - { - let raw_ptr = ptr.as_ptr() as *mut u8; - match Layout::array::(n) { - Some(ref k) if k.size() > 0 => { - Ok(self.dealloc(raw_ptr, k.clone())) - } - _ => { - Err(AllocErr::invalid_input("invalid layout for dealloc_array")) - } - } - } -} diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index c13ad39e5e1..9296a113071 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -19,7 +19,7 @@ use core::intrinsics::{min_align_of_val, size_of_val}; use core::mem::{self, ManuallyDrop}; use core::usize; -pub use allocator::*; +pub use core::heap::*; #[doc(hidden)] pub mod __core { pub use core::*; diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 19d64d8fea9..5594caa65b9 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -81,6 +81,7 @@ #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] +#![feature(allocator_api)] #![feature(allow_internal_unstable)] #![feature(ascii_ctype)] #![feature(box_into_raw_non_null)] @@ -145,9 +146,9 @@ extern crate std_unicode; #[macro_use] mod macros; -// Allocator trait and helper struct definitions - -pub mod allocator; +#[rustc_deprecated(since = "1.27.0", reason = "use the heap module in core, alloc, or std instead")] +#[unstable(feature = "allocator_api", issue = "32838")] +pub use core::heap as allocator; // Heaps provided for low-level allocation strategies diff --git a/src/libcore/heap.rs b/src/libcore/heap.rs new file mode 100644 index 00000000000..dae60b1647f --- /dev/null +++ b/src/libcore/heap.rs @@ -0,0 +1,1082 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked \ + slightly, especially to possibly take into account the \ + types being stored to make room for a future \ + tracing garbage collector", + issue = "32838")] + +use cmp; +use fmt; +use mem; +use usize; +use ptr::{self, NonNull}; + +/// Represents the combination of a starting address and +/// a total capacity of the returned block. +#[derive(Debug)] +pub struct Excess(pub *mut u8, pub usize); + +fn size_align() -> (usize, usize) { + (mem::size_of::(), mem::align_of::()) +} + +/// Layout of a block of memory. +/// +/// An instance of `Layout` describes a particular layout of memory. +/// You build a `Layout` up as an input to give to an allocator. +/// +/// All layouts have an associated non-negative size and a +/// power-of-two alignment. +/// +/// (Note however that layouts are *not* required to have positive +/// size, even though many allocators require that all memory +/// requests have positive size. A caller to the `Alloc::alloc` +/// method must either ensure that conditions like this are met, or +/// use specific allocators with looser requirements.) +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Layout { + // size of the requested block of memory, measured in bytes. + size: usize, + + // alignment of the requested block of memory, measured in bytes. + // we ensure that this is always a power-of-two, because API's + // like `posix_memalign` require it and it is a reasonable + // constraint to impose on Layout constructors. + // + // (However, we do not analogously require `align >= sizeof(void*)`, + // even though that is *also* a requirement of `posix_memalign`.) + align: usize, +} + + +// FIXME: audit default implementations for overflow errors, +// (potentially switching to overflowing_add and +// overflowing_mul as necessary). + +impl Layout { + /// Constructs a `Layout` from a given `size` and `align`, + /// or returns `None` if any of the following conditions + /// are not met: + /// + /// * `align` must be a power of two, + /// + /// * `align` must not exceed 231 (i.e. `1 << 31`), + /// + /// * `size`, when rounded up to the nearest multiple of `align`, + /// must not overflow (i.e. the rounded value must be less than + /// `usize::MAX`). + #[inline] + pub fn from_size_align(size: usize, align: usize) -> Option { + if !align.is_power_of_two() { + return None; + } + + if align > (1 << 31) { + return None; + } + + // (power-of-two implies align != 0.) + + // Rounded up size is: + // size_rounded_up = (size + align - 1) & !(align - 1); + // + // We know from above that align != 0. If adding (align - 1) + // does not overflow, then rounding up will be fine. + // + // Conversely, &-masking with !(align - 1) will subtract off + // only low-order-bits. Thus if overflow occurs with the sum, + // the &-mask cannot subtract enough to undo that overflow. + // + // Above implies that checking for summation overflow is both + // necessary and sufficient. + if size > usize::MAX - (align - 1) { + return None; + } + + unsafe { + Some(Layout::from_size_align_unchecked(size, align)) + } + } + + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify that `align` is + /// a power-of-two that is also less than or equal to 231, nor + /// that `size` aligned to `align` fits within the address space + /// (i.e. the `Layout::from_size_align` preconditions). + #[inline] + pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { + Layout { size: size, align: align } + } + + /// The minimum size in bytes for a memory block of this layout. + #[inline] + pub fn size(&self) -> usize { self.size } + + /// The minimum byte alignment for a memory block of this layout. + #[inline] + pub fn align(&self) -> usize { self.align } + + /// Constructs a `Layout` suitable for holding a value of type `T`. + pub fn new() -> Self { + let (size, align) = size_align::(); + Layout::from_size_align(size, align).unwrap() + } + + /// Produces layout describing a record that could be used to + /// allocate backing structure for `T` (which could be a trait + /// or other unsized type like a slice). + pub fn for_value(t: &T) -> Self { + let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); + Layout::from_size_align(size, align).unwrap() + } + + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `align` (measured in bytes). + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// # Panics + /// + /// Panics if the combination of `self.size` and the given `align` + /// violates the conditions listed in `from_size_align`. + #[inline] + pub fn align_to(&self, align: usize) -> Self { + Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap() + } + + /// Returns the amount of padding we must insert after `self` + /// to ensure that the following address will satisfy `align` + /// (measured in bytes). + /// + /// E.g. if `self.size` is 9, then `self.padding_needed_for(4)` + /// returns 3, because that is the minimum number of bytes of + /// padding required to get a 4-aligned address (assuming that the + /// corresponding memory block starts at a 4-aligned address). + /// + /// The return value of this function has no meaning if `align` is + /// not a power-of-two. + /// + /// Note that the utility of the returned value requires `align` + /// to be less than or equal to the alignment of the starting + /// address for the whole allocated block of memory. One way to + /// satisfy this constraint is to ensure `align <= self.align`. + #[inline] + pub fn padding_needed_for(&self, align: usize) -> usize { + let len = self.size(); + + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask wth `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + return len_rounded_up.wrapping_sub(len); + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with a suitable amount of padding between each to + /// ensure that each instance is given its requested size and + /// alignment. On success, returns `(k, offs)` where `k` is the + /// layout of the array and `offs` is the distance between the start + /// of each element in the array. + /// + /// On arithmetic overflow, returns `None`. + #[inline] + pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { + let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?; + let alloc_size = padded_size.checked_mul(n)?; + + // We can assume that `self.align` is a power-of-two that does + // not exceed 231. Furthermore, `alloc_size` has already been + // rounded up to a multiple of `self.align`; therefore, the + // call to `Layout::from_size_align` below should never panic. + Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) + } + + /// Creates a layout describing the record for `self` followed by + /// `next`, including any necessary padding to ensure that `next` + /// will be properly aligned. Note that the result layout will + /// satisfy the alignment properties of both `self` and `next`. + /// + /// Returns `Some((k, offset))`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// On arithmetic overflow, returns `None`. + pub fn extend(&self, next: Self) -> Option<(Self, usize)> { + let new_align = cmp::max(self.align, next.align); + let realigned = Layout::from_size_align(self.size, new_align)?; + + let pad = realigned.padding_needed_for(next.align); + + let offset = self.size.checked_add(pad)?; + let new_size = offset.checked_add(next.size)?; + + let layout = Layout::from_size_align(new_size, new_align)?; + Some((layout, offset)) + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with no padding between each instance. + /// + /// Note that, unlike `repeat`, `repeat_packed` does not guarantee + /// that the repeated instances of `self` will be properly + /// aligned, even if a given instance of `self` is properly + /// aligned. In other words, if the layout returned by + /// `repeat_packed` is used to allocate an array, it is not + /// guaranteed that all elements in the array will be properly + /// aligned. + /// + /// On arithmetic overflow, returns `None`. + pub fn repeat_packed(&self, n: usize) -> Option { + let size = self.size().checked_mul(n)?; + Layout::from_size_align(size, self.align) + } + + /// Creates a layout describing the record for `self` followed by + /// `next` with no additional padding between the two. Since no + /// padding is inserted, the alignment of `next` is irrelevant, + /// and is not incorporated *at all* into the resulting layout. + /// + /// Returns `(k, offset)`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// (The `offset` is always the same as `self.size()`; we use this + /// signature out of convenience in matching the signature of + /// `extend`.) + /// + /// On arithmetic overflow, returns `None`. + pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { + let new_size = self.size().checked_add(next.size())?; + let layout = Layout::from_size_align(new_size, self.align)?; + Some((layout, self.size())) + } + + /// Creates a layout describing the record for a `[T; n]`. + /// + /// On arithmetic overflow, returns `None`. + pub fn array(n: usize) -> Option { + Layout::new::() + .repeat(n) + .map(|(k, offs)| { + debug_assert!(offs == mem::size_of::()); + k + }) + } +} + +/// The `AllocErr` error specifies whether an allocation failure is +/// specifically due to resource exhaustion or if it is due to +/// something wrong when combining the given input arguments with this +/// allocator. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum AllocErr { + /// Error due to hitting some resource limit or otherwise running + /// out of memory. This condition strongly implies that *some* + /// series of deallocations would allow a subsequent reissuing of + /// the original allocation request to succeed. + Exhausted { request: Layout }, + + /// Error due to allocator being fundamentally incapable of + /// satisfying the original request. This condition implies that + /// such an allocation request will never succeed on the given + /// allocator, regardless of environment, memory pressure, or + /// other contextual conditions. + /// + /// For example, an allocator that does not support requests for + /// large memory blocks might return this error variant. + Unsupported { details: &'static str }, +} + +impl AllocErr { + #[inline] + pub fn invalid_input(details: &'static str) -> Self { + AllocErr::Unsupported { details: details } + } + #[inline] + pub fn is_memory_exhausted(&self) -> bool { + if let AllocErr::Exhausted { .. } = *self { true } else { false } + } + #[inline] + pub fn is_request_unsupported(&self) -> bool { + if let AllocErr::Unsupported { .. } = *self { true } else { false } + } + #[inline] + pub fn description(&self) -> &str { + match *self { + AllocErr::Exhausted { .. } => "allocator memory exhausted", + AllocErr::Unsupported { .. } => "unsupported allocator request", + } + } +} + +// (we need this for downstream impl of trait Error) +impl fmt::Display for AllocErr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +/// The `CannotReallocInPlace` error is used when `grow_in_place` or +/// `shrink_in_place` were unable to reuse the given memory block for +/// a requested layout. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CannotReallocInPlace; + +impl CannotReallocInPlace { + pub fn description(&self) -> &str { + "cannot reallocate allocator's memory in place" + } +} + +// (we need this for downstream impl of trait Error) +impl fmt::Display for CannotReallocInPlace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +/// Augments `AllocErr` with a CapacityOverflow variant. +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +pub enum CollectionAllocErr { + /// Error due to the computed capacity exceeding the collection's maximum + /// (usually `isize::MAX` bytes). + CapacityOverflow, + /// Error due to the allocator (see the `AllocErr` type's docs). + AllocErr(AllocErr), +} + +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From for CollectionAllocErr { + fn from(err: AllocErr) -> Self { + CollectionAllocErr::AllocErr(err) + } +} + +/// An implementation of `Alloc` can allocate, reallocate, and +/// deallocate arbitrary blocks of data described via `Layout`. +/// +/// Some of the methods require that a memory block be *currently +/// allocated* via an allocator. This means that: +/// +/// * the starting address for that memory block was previously +/// returned by a previous call to an allocation method (`alloc`, +/// `alloc_zeroed`, `alloc_excess`, `alloc_one`, `alloc_array`) or +/// reallocation method (`realloc`, `realloc_excess`, or +/// `realloc_array`), and +/// +/// * the memory block has not been subsequently deallocated, where +/// blocks are deallocated either by being passed to a deallocation +/// method (`dealloc`, `dealloc_one`, `dealloc_array`) or by being +/// passed to a reallocation method (see above) that returns `Ok`. +/// +/// A note regarding zero-sized types and zero-sized layouts: many +/// methods in the `Alloc` trait state that allocation requests +/// must be non-zero size, or else undefined behavior can result. +/// +/// * However, some higher-level allocation methods (`alloc_one`, +/// `alloc_array`) are well-defined on zero-sized types and can +/// optionally support them: it is left up to the implementor +/// whether to return `Err`, or to return `Ok` with some pointer. +/// +/// * If an `Alloc` implementation chooses to return `Ok` in this +/// case (i.e. the pointer denotes a zero-sized inaccessible block) +/// then that returned pointer must be considered "currently +/// allocated". On such an allocator, *all* methods that take +/// currently-allocated pointers as inputs must accept these +/// zero-sized pointers, *without* causing undefined behavior. +/// +/// * In other words, if a zero-sized pointer can flow out of an +/// allocator, then that allocator must likewise accept that pointer +/// flowing back into its deallocation and reallocation methods. +/// +/// Some of the methods require that a layout *fit* a memory block. +/// What it means for a layout to "fit" a memory block means (or +/// equivalently, for a memory block to "fit" a layout) is that the +/// following two conditions must hold: +/// +/// 1. The block's starting address must be aligned to `layout.align()`. +/// +/// 2. The block's size must fall in the range `[use_min, use_max]`, where: +/// +/// * `use_min` is `self.usable_size(layout).0`, and +/// +/// * `use_max` is the capacity that was (or would have been) +/// returned when (if) the block was allocated via a call to +/// `alloc_excess` or `realloc_excess`. +/// +/// Note that: +/// +/// * the size of the layout most recently used to allocate the block +/// is guaranteed to be in the range `[use_min, use_max]`, and +/// +/// * a lower-bound on `use_max` can be safely approximated by a call to +/// `usable_size`. +/// +/// * 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 + // behavior in response to a zero size allocation request ; + // e.g. in C, `malloc` of 0 will either return a null pointer or a + // unique pointer, but will not have arbitrary undefined + // behavior. Rust should consider revising the alloc::heap crate + // to reflect this reality.) + + /// Returns a pointer meeting the size and alignment guarantees of + /// `layout`. + /// + /// If this method returns an `Ok(addr)`, then the `addr` returned + /// will be non-null address pointing to a block of storage + /// suitable for holding an instance of `layout`. + /// + /// The returned block of storage may or may not have its contents + /// initialized. (Extension subtraits might restrict this + /// behavior, e.g. to ensure initialization to particular sets of + /// bit patterns.) + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints. + /// + /// Implementations are encouraged to return `Err` on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + + /// Deallocate the memory referenced by `ptr`. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must denote a block of memory currently allocated via + /// this allocator, + /// + /// * `layout` must *fit* that block of memory, + /// + /// * In addition to fitting the block of memory `layout`, the + /// alignment of the `layout` must match the alignment used + /// to allocate that block of memory. + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + + /// Allocator-specific method for signaling an out-of-memory + /// condition. + /// + /// `oom` aborts the thread or process, optionally performing + /// cleanup or logging diagnostic information before panicking or + /// aborting. + /// + /// `oom` is meant to be used by clients unable to cope with an + /// unsatisfied allocation request (signaled by an error such as + /// `AllocErr::Exhausted`), and wish to abandon computation rather + /// than attempt to recover locally. Such clients should pass the + /// signaling error value back into `oom`, where the allocator + /// may incorporate that error value into its diagnostic report + /// before aborting. + /// + /// Implementations of the `oom` method are discouraged from + /// infinitely regressing in nested calls to `oom`. In + /// practice this means implementors should eschew allocating, + /// especially from `self` (directly or indirectly). + /// + /// Implementations of the allocation and reallocation methods + /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from + /// panicking (or aborting) in the event of memory exhaustion; + /// instead they should return an appropriate error from the + /// invoked method, and let the client decide whether to invoke + /// this `oom` method in response. + fn oom(&mut self, _: AllocErr) -> ! { + unsafe { ::intrinsics::abort() } + } + + // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == + // usable_size + + /// Returns bounds on the guaranteed usable size of a successful + /// allocation created with the specified `layout`. + /// + /// In particular, if one has a memory block allocated via a given + /// allocator `a` and layout `k` where `a.usable_size(k)` returns + /// `(l, u)`, then one can pass that block to `a.dealloc()` with a + /// layout in the size range [l, u]. + /// + /// (All implementors of `usable_size` must ensure that + /// `l <= k.size() <= u`) + /// + /// Both the lower- and upper-bounds (`l` and `u` respectively) + /// are provided, because an allocator based on size classes could + /// misbehave if one attempts to deallocate a block without + /// providing a correct value for its size (i.e., one within the + /// range `[l, u]`). + /// + /// Clients who wish to make use of excess capacity are encouraged + /// to use the `alloc_excess` and `realloc_excess` instead, as + /// this method is constrained to report conservative values that + /// serve as valid bounds for *all possible* allocation method + /// calls. + /// + /// However, for clients that do not wish to track the capacity + /// returned by `alloc_excess` locally, this method is likely to + /// produce useful results. + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + (layout.size(), layout.size()) + } + + // == METHODS FOR MEMORY REUSE == + // realloc. alloc_excess, realloc_excess + + /// Returns a pointer suitable for holding data described by + /// `new_layout`, meeting its size and alignment guarantees. To + /// accomplish this, this may extend or shrink the allocation + /// referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then ownership of the memory block + /// referenced by `ptr` has been transferred to this + /// allocator. The memory may or may not have been freed, and + /// should be considered unusable (unless of course it was + /// transferred back to the caller again via the return value of + /// this method). + /// + /// If this method returns `Err`, then ownership of the memory + /// block has not been transferred to this allocator, and the + /// contents of the memory block are unaltered. + /// + /// For best results, `new_layout` should not impose a different + /// alignment constraint than `layout`. (In other words, + /// `new_layout.align()` should equal `layout.align()`.) However, + /// behavior is well-defined (though underspecified) when this + /// constraint is violated; further discussion below. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` + /// argument need not fit it.) + /// + /// * `new_layout` must have size greater than zero. + /// + /// * the alignment of `new_layout` is non-zero. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returns `Err` only if `new_layout` does not match the + /// alignment of `layout`, or does not meet the allocator's size + /// and alignment constraints of the allocator, or if reallocation + /// otherwise fails. + /// + /// (Note the previous sentence did not say "if and only if" -- in + /// particular, an implementation of this method *can* return `Ok` + /// if `new_layout.align() != old_layout.align()`; or it can + /// return `Err` in that scenario, depending on whether this + /// allocator can dynamically adjust the alignment constraint for + /// the block.) + /// + /// Implementations are encouraged to return `Err` on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + let new_size = new_layout.size(); + let old_size = layout.size(); + let aligns_match = layout.align == new_layout.align; + + if new_size >= old_size && aligns_match { + if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { + return Ok(ptr); + } + } else if new_size < old_size && aligns_match { + if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { + return Ok(ptr); + } + } + + // otherwise, fall back on alloc + copy + dealloc. + let result = self.alloc(new_layout); + if let Ok(new_ptr) = result { + ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); + self.dealloc(ptr, layout); + } + result + } + + /// Behaves like `alloc`, but also ensures that the contents + /// are set to zero before being returned. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let size = layout.size(); + let p = self.alloc(layout); + if let Ok(p) = p { + ptr::write_bytes(p, 0, size); + } + p + } + + /// Behaves like `alloc`, but also returns the whole size of + /// the returned block. For some `layout` inputs, like arrays, this + /// may include extra storage usable for additional data. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + let usable_size = self.usable_size(&layout); + self.alloc(layout).map(|p| Excess(p, usable_size.1)) + } + + /// Behaves like `realloc`, but also returns the whole size of + /// the returned block. For some `layout` inputs, like arrays, this + /// may include extra storage usable for additional data. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `realloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `realloc`. + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + let usable_size = self.usable_size(&new_layout); + self.realloc(ptr, layout, new_layout) + .map(|p| Excess(p, usable_size.1)) + } + + /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_layout`, and thus can + /// be used to carry data of that layout. (The allocator is allowed to + /// expend effort to accomplish this, such as extending the memory block to + /// include successor blocks, or virtual memory tricks.) + /// + /// Regardless of what this method returns, ownership of the + /// memory block referenced by `ptr` has not been transferred, and + /// the contents of the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_layout` argument need not fit it, + /// + /// * `new_layout.size()` must not be less than `layout.size()`, + /// + /// * `new_layout.align()` must equal `layout.align()`. + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `grow_in_place` failures without aborting, or to fall back on + /// another reallocation method before resorting to an abort. + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_layout.size >= layout.size); + debug_assert!(new_layout.align == layout.align); + let (_l, u) = self.usable_size(&layout); + // _l <= layout.size() [guaranteed by usable_size()] + // layout.size() <= new_layout.size() [required by this method] + if new_layout.size <= u { + return Ok(()); + } else { + return Err(CannotReallocInPlace); + } + } + + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_layout`, and + /// thus can only be used to carry data of that smaller + /// layout. (The allocator is allowed to take advantage of this, + /// carving off portions of the block for reuse elsewhere.) The + /// truncated contents of the block within the smaller layout are + /// unaltered, and ownership of block has not been transferred. + /// + /// If this returns `Err`, then the memory block is considered to + /// still represent the original (larger) `layout`. None of the + /// block has been carved off for reuse elsewhere, ownership of + /// the memory block has not been transferred, and the contents of + /// the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_layout` argument need not fit it, + /// + /// * `new_layout.size()` must not be greater than `layout.size()` + /// (and must be greater than zero), + /// + /// * `new_layout.align()` must equal `layout.align()`. + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `shrink_in_place` failures without aborting, or to fall back + /// on another reallocation method before resorting to an abort. + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_layout.size <= layout.size); + debug_assert!(new_layout.align == layout.align); + let (l, _u) = self.usable_size(&layout); + // layout.size() <= _u [guaranteed by usable_size()] + // new_layout.size() <= layout.size() [required by this method] + if l <= new_layout.size { + return Ok(()); + } else { + return Err(CannotReallocInPlace); + } + } + + + // == COMMON USAGE PATTERNS == + // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array + + /// Allocates a block suitable for holding an instance of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` + /// must be considered "currently allocated" and must be + /// acceptable input to methods such as `realloc` or `dealloc`, + /// *even if* `T` is a zero-sized type. In other words, if your + /// `Alloc` implementation overrides this method in a manner + /// that can return a zero-sized `ptr`, then all reallocation and + /// deallocation methods need to be similarly overridden to accept + /// such values as input. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `T` does not meet allocator's size or alignment constraints. + /// + /// For zero-sized `T`, may return either of `Ok` or `Err`, but + /// will *not* yield undefined behavior. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + fn alloc_one(&mut self) -> Result, AllocErr> + where Self: Sized + { + let k = Layout::new::(); + if k.size() > 0 { + unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } + } else { + Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) + } + } + + /// Deallocates a block suitable for holding an instance of `T`. + /// + /// The given block must have been produced by this allocator, + /// and must be suitable for storing a `T` (in terms of alignment + /// as well as minimum and maximum size); otherwise yields + /// undefined behavior. + /// + /// Captures a common usage pattern for allocators. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure both: + /// + /// * `ptr` must denote a block of memory currently allocated via this allocator + /// + /// * the layout of `T` must *fit* that block of memory. + unsafe fn dealloc_one(&mut self, ptr: NonNull) + where Self: Sized + { + let raw_ptr = ptr.as_ptr() as *mut u8; + let k = Layout::new::(); + if k.size() > 0 { + self.dealloc(raw_ptr, k); + } + } + + /// Allocates a block suitable for holding `n` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` + /// must be considered "currently allocated" and must be + /// acceptable input to methods such as `realloc` or `dealloc`, + /// *even if* `T` is a zero-sized type. In other words, if your + /// `Alloc` implementation overrides this method in a manner + /// that can return a zero-sized `ptr`, then all reallocation and + /// deallocation methods need to be similarly overridden to accept + /// such values as input. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `[T; n]` does not meet allocator's size or alignment + /// constraints. + /// + /// For zero-sized `T` or `n == 0`, may return either of `Ok` or + /// `Err`, but will *not* yield undefined behavior. + /// + /// Always returns `Err` on arithmetic overflow. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + fn alloc_array(&mut self, n: usize) -> Result, AllocErr> + where Self: Sized + { + match Layout::array::(n) { + Some(ref layout) if layout.size() > 0 => { + unsafe { + self.alloc(layout.clone()) + .map(|p| { + NonNull::new_unchecked(p as *mut T) + }) + } + } + _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), + } + } + + /// Reallocates a block previously suitable for holding `n_old` + /// instances of `T`, returning a block suitable for holding + /// `n_new` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * the layout of `[T; n_old]` must *fit* that block of memory. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `[T; n_new]` does not meet allocator's size or alignment + /// constraints. + /// + /// For zero-sized `T` or `n_new == 0`, may return either of `Ok` or + /// `Err`, but will *not* yield undefined behavior. + /// + /// Always returns `Err` on arithmetic overflow. + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc_array(&mut self, + ptr: NonNull, + n_old: usize, + n_new: usize) -> Result, AllocErr> + where Self: Sized + { + match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { + (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { + self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) + .map(|p| NonNull::new_unchecked(p as *mut T)) + } + _ => { + Err(AllocErr::invalid_input("invalid layout for realloc_array")) + } + } + } + + /// Deallocates a block suitable for holding `n` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure both: + /// + /// * `ptr` must denote a block of memory currently allocated via this allocator + /// + /// * the layout of `[T; n]` must *fit* that block of memory. + /// + /// # Errors + /// + /// Returning `Err` indicates that either `[T; n]` or the given + /// memory block does not meet allocator's size or alignment + /// constraints. + /// + /// Always returns `Err` on arithmetic overflow. + unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> + where Self: Sized + { + let raw_ptr = ptr.as_ptr() as *mut u8; + match Layout::array::(n) { + Some(ref k) if k.size() > 0 => { + Ok(self.dealloc(raw_ptr, k.clone())) + } + _ => { + Err(AllocErr::invalid_input("invalid layout for dealloc_array")) + } + } + } +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 11fecde3951..c77402ed442 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -185,6 +185,10 @@ pub mod hash; pub mod fmt; pub mod time; +/* Heap memory allocator trait */ +#[allow(missing_docs)] +pub mod heap; + // note: does not need to be public mod char_private; mod iter_private; diff --git a/src/libstd/heap.rs b/src/libstd/heap.rs index 4d5e4df6f95..4a391372c3a 100644 --- a/src/libstd/heap.rs +++ b/src/libstd/heap.rs @@ -12,8 +12,9 @@ #![unstable(issue = "32838", feature = "allocator_api")] -pub use alloc::heap::{Heap, Alloc, Layout, Excess, CannotReallocInPlace, AllocErr}; +pub use alloc::heap::Heap; pub use alloc_system::System; +pub use core::heap::*; #[cfg(not(test))] #[doc(hidden)] -- cgit 1.4.1-3-g733a5 From fb7deda27419eae61da3cbf5a5b1b4f51ae16d04 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Fri, 30 Mar 2018 23:06:05 -0700 Subject: Add #[must_use] to a few standard library methods Chosen to start a precedent of using it on ones that are potentially-expensive and where using it for side effects is particularly discouraged. Discuss :) --- src/liballoc/borrow.rs | 1 + src/libcore/clone.rs | 1 + src/libcore/iter/iterator.rs | 1 + src/librustc_mir/build/mod.rs | 2 +- 4 files changed, 4 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/borrow.rs b/src/liballoc/borrow.rs index acae0daa86b..c6741ddb822 100644 --- a/src/liballoc/borrow.rs +++ b/src/liballoc/borrow.rs @@ -59,6 +59,7 @@ pub trait ToOwned { /// let vv: Vec = v.to_owned(); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "cloning is often expensive and is not expected to have side effects"] fn to_owned(&self) -> Self::Owned; /// Uses borrowed data to replace owned data, usually by cloning. diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index d25f498b99e..c175ae15d28 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -105,6 +105,7 @@ pub trait Clone : Sized { /// assert_eq!("Hello", hello.clone()); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "cloning is often expensive and is not expected to have side effects"] fn clone(&self) -> Self; /// Performs copy-assignment from `source`. diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index 31f77f92435..42fd9051292 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -1368,6 +1368,7 @@ pub trait Iterator { /// [`Result`]: ../../std/result/enum.Result.html #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[must_use = "if you really need to exhaust the iterator, consider `.for_each(drop)` instead"] fn collect>(self) -> B where Self: Sized { FromIterator::from_iter(self) } diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index 8494c043f90..6f5fcc9e421 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -317,7 +317,7 @@ newtype_index!(ScopeId); /// macro (and methods below) makes working with `BlockAnd` much more /// convenient. -#[must_use] // if you don't use one of these results, you're leaving a dangling edge +#[must_use = "if you don't use one of these results, you're leaving a dangling edge"] struct BlockAnd(BasicBlock, T); trait BlockAndExtension { -- cgit 1.4.1-3-g733a5 From b394165538bc52063f79a1820135cfefa19370e7 Mon Sep 17 00:00:00 2001 From: Scott McMurray Date: Sat, 31 Mar 2018 22:35:37 -0700 Subject: Deprecate offset_to; switch core&alloc to using offset_from instead Bonus: might make code than uses `.len()` on slice iterators faster --- src/liballoc/lib.rs | 2 +- src/liballoc/vec.rs | 7 ++++--- src/libcore/ptr.rs | 12 ++++++++---- src/libcore/slice/mod.rs | 11 ++++++----- 4 files changed, 19 insertions(+), 13 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index e98b58994bf..009441e1680 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -103,13 +103,13 @@ #![feature(lang_items)] #![feature(needs_allocator)] #![feature(nonzero)] -#![feature(offset_to)] #![feature(optin_builtin_traits)] #![feature(pattern)] #![feature(pin)] #![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(ptr_internals)] +#![feature(ptr_offset_from)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] #![feature(slice_rsplit)] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2eedb964f88..d8ec956df8a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2338,9 +2338,10 @@ impl Iterator for IntoIter { #[inline] fn size_hint(&self) -> (usize, Option) { - let exact = match self.ptr.offset_to(self.end) { - Some(x) => x as usize, - None => (self.end as usize).wrapping_sub(self.ptr as usize), + let exact = if mem::size_of::() == 0 { + (self.end as usize).wrapping_sub(self.ptr as usize) + } else { + unsafe { self.end.offset_from(self.ptr) as usize } }; (exact, Some(exact)) } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 5a54de06b5e..c1e150e9fb9 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -677,6 +677,7 @@ impl *const T { /// /// ``` /// #![feature(offset_to)] + /// #![allow(deprecated)] /// /// fn main() { /// let a = [0; 5]; @@ -689,14 +690,15 @@ impl *const T { /// } /// ``` #[unstable(feature = "offset_to", issue = "41079")] + #[rustc_deprecated(since = "1.27.0", reason = "Replaced by `wrapping_offset_from`, with the \ + opposite argument order. If you're writing unsafe code, consider `offset_from`.")] #[inline] pub fn offset_to(self, other: *const T) -> Option where T: Sized { let size = mem::size_of::(); if size == 0 { None } else { - let diff = (other as isize).wrapping_sub(self as isize); - Some(diff / size as isize) + Some(other.wrapping_offset_from(self)) } } @@ -1442,6 +1444,7 @@ impl *mut T { /// /// ``` /// #![feature(offset_to)] + /// #![allow(deprecated)] /// /// fn main() { /// let mut a = [0; 5]; @@ -1454,14 +1457,15 @@ impl *mut T { /// } /// ``` #[unstable(feature = "offset_to", issue = "41079")] + #[rustc_deprecated(since = "1.27.0", reason = "Replaced by `wrapping_offset_from`, with the \ + opposite argument order. If you're writing unsafe code, consider `offset_from`.")] #[inline] pub fn offset_to(self, other: *const T) -> Option where T: Sized { let size = mem::size_of::(); if size == 0 { None } else { - let diff = (other as isize).wrapping_sub(self as isize); - Some(diff / size as isize) + Some(other.wrapping_offset_from(self)) } } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 0f1b7cb8fcc..0a22028da81 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1185,7 +1185,7 @@ macro_rules! iterator { #[inline] fn size_hint(&self) -> (usize, Option) { - let exact = ptrdistance(self.ptr, self.end); + let exact = unsafe { ptrdistance(self.ptr, self.end) }; (exact, Some(exact)) } @@ -1593,10 +1593,11 @@ unsafe impl<'a, T> TrustedLen for IterMut<'a, T> {} // Return the number of elements of `T` from `start` to `end`. // Return the arithmetic difference if `T` is zero size. #[inline(always)] -fn ptrdistance(start: *const T, end: *const T) -> usize { - match start.offset_to(end) { - Some(x) => x as usize, - None => (end as usize).wrapping_sub(start as usize), +unsafe fn ptrdistance(start: *const T, end: *const T) -> usize { + if mem::size_of::() == 0 { + (end as usize).wrapping_sub(start as usize) + } else { + end.offset_from(start) as usize } } -- cgit 1.4.1-3-g733a5 From 360f2f036d14889f4de37a8b4b6aad0d09772aad Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sat, 31 Mar 2018 23:19:02 +0200 Subject: Inline most of the code paths for conversions with boxed slices This helps with the specific problem described in #49541, obviously without making any large change to how inlining works in the general case. Everything involved in the conversions is made `#[inline]`, except for the `>::into_boxed_slice` entry point which is made `#[inline(always)]` after checking that duplicating the function mentioned in the issue prevented its inlining if I only annotate it with `#[inline]`. For the record, that function was: ```rust pub fn foo() -> Box<[u8]> { vec![0].into_boxed_slice() } ``` To help the inliner's job, we also hoist a `self.capacity() != self.len` check in `>::shrink_to_fit` and mark it as `#[inline]` too. --- src/liballoc/boxed.rs | 2 ++ src/liballoc/str.rs | 3 +++ src/liballoc/string.rs | 1 + src/liballoc/vec.rs | 6 +++++- 4 files changed, 11 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fdc3ef4efb8..2fc60a799f3 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -577,6 +577,7 @@ impl<'a, T: Copy> From<&'a [T]> for Box<[T]> { #[stable(feature = "box_from_slice", since = "1.17.0")] impl<'a> From<&'a str> for Box { + #[inline] fn from(s: &'a str) -> Box { unsafe { from_boxed_utf8_unchecked(Box::from(s.as_bytes())) } } @@ -584,6 +585,7 @@ impl<'a> From<&'a str> for Box { #[stable(feature = "boxed_str_conv", since = "1.19.0")] impl From> for Box<[u8]> { + #[inline] fn from(s: Box) -> Self { unsafe { Box::from_raw(Box::into_raw(s) as *mut [u8]) } } diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index d5ef41df0d8..6f05c52eb57 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -1811,6 +1811,7 @@ impl str { /// assert_eq!(*boxed_bytes, *s.as_bytes()); /// ``` #[stable(feature = "str_box_extras", since = "1.20.0")] + #[inline] pub fn into_boxed_bytes(self: Box) -> Box<[u8]> { self.into() } @@ -2049,6 +2050,7 @@ impl str { /// assert_eq!(boxed_str.into_string(), string); /// ``` #[stable(feature = "box_str", since = "1.4.0")] + #[inline] pub fn into_string(self: Box) -> String { let slice = Box::<[u8]>::from(self); unsafe { String::from_utf8_unchecked(slice.into_vec()) } @@ -2307,6 +2309,7 @@ impl str { /// assert_eq!("☺", &*smile); /// ``` #[stable(feature = "str_box_extras", since = "1.20.0")] +#[inline] pub unsafe fn from_boxed_utf8_unchecked(v: Box<[u8]>) -> Box { Box::from_raw(Box::into_raw(v) as *mut str) } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index aa202e23628..95d5b30b67d 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1587,6 +1587,7 @@ impl String { /// let b = s.into_boxed_str(); /// ``` #[stable(feature = "box_str", since = "1.4.0")] + #[inline] pub fn into_boxed_str(self) -> Box { let slice = self.vec.into_boxed_slice(); unsafe { from_boxed_utf8_unchecked(slice) } diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2eedb964f88..e3c036e6aac 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -582,8 +582,11 @@ impl Vec { /// assert!(vec.capacity() >= 3); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline] pub fn shrink_to_fit(&mut self) { - self.buf.shrink_to_fit(self.len); + if self.capacity() != self.len { + self.buf.shrink_to_fit(self.len); + } } /// Shrinks the capacity of the vector with a lower bound. @@ -636,6 +639,7 @@ impl Vec { /// assert_eq!(slice.into_vec().capacity(), 3); /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] pub fn into_boxed_slice(mut self) -> Box<[T]> { unsafe { self.shrink_to_fit(); -- cgit 1.4.1-3-g733a5 From cc939ac345091327b23f807b7d1f6a7c75c03f36 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 30 Mar 2018 16:10:47 +0900 Subject: Add vec![ptr::null{,_mut}(); n] optimization, like vec![0; n] vec![0; n], via implementations of SpecFromElem, has an optimization that uses with_capacity_zeroed instead of with_capacity, which will use calloc instead of malloc, and avoid an extra memset. This adds the same optimization for vec![ptr::null(); n] and vec![ptr::null_mut(); n], assuming their bit value is 0 (which is true on all currently supported platforms). This does so by adding an intermediate trait IsZero, which looks very much like nonzero::Zeroable, but that one is on the way out, and doesn't apply to pointers anyways. Adding such a trait allows to avoid repeating the logic using with_capacity_zeroed or with_capacity, or making the macro more complex to support generics. --- src/liballoc/vec.rs | 79 +++++++++++++++++++++++++++++++++++------------------ 1 file changed, 53 insertions(+), 26 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2eedb964f88..512c74d9d77 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1567,40 +1567,67 @@ impl SpecFromElem for u8 { } } -macro_rules! impl_spec_from_elem { +impl SpecFromElem for T { + #[inline] + fn from_elem(elem: T, n: usize) -> Vec { + if elem.is_zero() { + return Vec { + buf: RawVec::with_capacity_zeroed(n), + len: n, + } + } + let mut v = Vec::with_capacity(n); + v.extend_with(n, ExtendElement(elem)); + v + } +} + +unsafe trait IsZero { + /// Whether this value is zero + fn is_zero(&self) -> bool; +} + +macro_rules! impl_is_zero { ($t: ty, $is_zero: expr) => { - impl SpecFromElem for $t { + unsafe impl IsZero for $t { #[inline] - fn from_elem(elem: $t, n: usize) -> Vec<$t> { - if $is_zero(elem) { - return Vec { - buf: RawVec::with_capacity_zeroed(n), - len: n, - } - } - let mut v = Vec::with_capacity(n); - v.extend_with(n, ExtendElement(elem)); - v + fn is_zero(&self) -> bool { + $is_zero(*self) } } - }; + } } -impl_spec_from_elem!(i8, |x| x == 0); -impl_spec_from_elem!(i16, |x| x == 0); -impl_spec_from_elem!(i32, |x| x == 0); -impl_spec_from_elem!(i64, |x| x == 0); -impl_spec_from_elem!(i128, |x| x == 0); -impl_spec_from_elem!(isize, |x| x == 0); +impl_is_zero!(i8, |x| x == 0); +impl_is_zero!(i16, |x| x == 0); +impl_is_zero!(i32, |x| x == 0); +impl_is_zero!(i64, |x| x == 0); +impl_is_zero!(i128, |x| x == 0); +impl_is_zero!(isize, |x| x == 0); -impl_spec_from_elem!(u16, |x| x == 0); -impl_spec_from_elem!(u32, |x| x == 0); -impl_spec_from_elem!(u64, |x| x == 0); -impl_spec_from_elem!(u128, |x| x == 0); -impl_spec_from_elem!(usize, |x| x == 0); +impl_is_zero!(u16, |x| x == 0); +impl_is_zero!(u32, |x| x == 0); +impl_is_zero!(u64, |x| x == 0); +impl_is_zero!(u128, |x| x == 0); +impl_is_zero!(usize, |x| x == 0); + +impl_is_zero!(f32, |x: f32| x.to_bits() == 0); +impl_is_zero!(f64, |x: f64| x.to_bits() == 0); + +unsafe impl IsZero for *const T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} + +unsafe impl IsZero for *mut T { + #[inline] + fn is_zero(&self) -> bool { + (*self).is_null() + } +} -impl_spec_from_elem!(f32, |x: f32| x.to_bits() == 0); -impl_spec_from_elem!(f64, |x: f64| x.to_bits() == 0); //////////////////////////////////////////////////////////////////////////////// // Common trait implementations for Vec -- cgit 1.4.1-3-g733a5 From 0df837f79289819f9b671b67d4e63dfe5c80d419 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Mon, 2 Apr 2018 10:44:38 +0900 Subject: Add vec!['\0'; n] optimization, like vec![0; n] Similarly to vec![ptr::null{,_mut}(); n] in previous change, this adds the optimization for vec!['\0'; n]. --- src/liballoc/vec.rs | 2 ++ 1 file changed, 2 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 512c74d9d77..515b79e5acd 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1611,6 +1611,8 @@ impl_is_zero!(u64, |x| x == 0); impl_is_zero!(u128, |x| x == 0); impl_is_zero!(usize, |x| x == 0); +impl_is_zero!(char, |x| x == '\0'); + impl_is_zero!(f32, |x: f32| x.to_bits() == 0); impl_is_zero!(f64, |x: f64| x.to_bits() == 0); -- cgit 1.4.1-3-g733a5 From 196b1426bec62b590df790c5f715d46075e01840 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Sun, 1 Apr 2018 22:50:22 -0600 Subject: Stabilize String::replace_range Fixes #44643 --- src/liballoc/string.rs | 11 +++++------ src/liballoc/tests/string.rs | 30 +++++++++++++++--------------- 2 files changed, 20 insertions(+), 21 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index aa202e23628..b95aae02894 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1517,7 +1517,7 @@ impl String { } } - /// Creates a splicing iterator that removes the specified range in the string, + /// Removes the specified range in the string, /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. /// @@ -1537,21 +1537,20 @@ impl String { /// Basic usage: /// /// ``` - /// #![feature(splice)] /// let mut s = String::from("α is alpha, β is beta"); /// let beta_offset = s.find('β').unwrap_or(s.len()); /// /// // Replace the range up until the β from the string - /// s.splice(..beta_offset, "Α is capital alpha; "); + /// s.replace_range(..beta_offset, "Α is capital alpha; "); /// assert_eq!(s, "Α is capital alpha; β is beta"); /// ``` - #[unstable(feature = "splice", reason = "recently added", issue = "44643")] - pub fn splice(&mut self, range: R, replace_with: &str) + #[stable(feature = "splice", since = "1.27.0")] + pub fn replace_range(&mut self, range: R, replace_with: &str) where R: RangeBounds { // Memory safety // - // The String version of Splice does not have the memory safety issues + // Replace_range does not have the memory safety issues of a vector Splice. // of the vector version. The data is just plain bytes. match range.start() { diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index d1e746ea43b..cb4a17a22d8 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -443,53 +443,53 @@ fn test_drain() { } #[test] -fn test_splice() { +fn test_replace_range() { let mut s = "Hello, world!".to_owned(); - s.splice(7..12, "世界"); + s.replace_range(7..12, "世界"); assert_eq!(s, "Hello, 世界!"); } #[test] #[should_panic] -fn test_splice_char_boundary() { +fn test_replace_range_char_boundary() { let mut s = "Hello, 世界!".to_owned(); - s.splice(..8, ""); + s.replace_range(..8, ""); } #[test] -fn test_splice_inclusive_range() { +fn test_replace_range_inclusive_range() { let mut v = String::from("12345"); - v.splice(2..=3, "789"); + v.replace_range(2..=3, "789"); assert_eq!(v, "127895"); - v.splice(1..=2, "A"); + v.replace_range(1..=2, "A"); assert_eq!(v, "1A895"); } #[test] #[should_panic] -fn test_splice_out_of_bounds() { +fn test_replace_range_out_of_bounds() { let mut s = String::from("12345"); - s.splice(5..6, "789"); + s.replace_range(5..6, "789"); } #[test] #[should_panic] -fn test_splice_inclusive_out_of_bounds() { +fn test_replace_range_inclusive_out_of_bounds() { let mut s = String::from("12345"); - s.splice(5..=5, "789"); + s.replace_range(5..=5, "789"); } #[test] -fn test_splice_empty() { +fn test_replace_range_empty() { let mut s = String::from("12345"); - s.splice(1..2, ""); + s.replace_range(1..2, ""); assert_eq!(s, "1345"); } #[test] -fn test_splice_unbounded() { +fn test_replace_range_unbounded() { let mut s = String::from("12345"); - s.splice(.., ""); + s.replace_range(.., ""); assert_eq!(s, ""); } -- cgit 1.4.1-3-g733a5 From b647583c2df155f4741fa2b3e1fa6e4fb1f5868f Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Mon, 2 Apr 2018 16:05:30 +0900 Subject: Use Alloc and Layout from core::heap. 94d1970bba87f2d2893f6e934e4c3f02ed50604d moved the alloc::allocator module to core::heap, moving e.g. Alloc and Layout out of the alloc crate. While alloc::heap reexports them, it's better to use them from where they really come from. --- src/liballoc/arc.rs | 3 ++- src/liballoc/boxed.rs | 3 ++- src/liballoc/btree/node.rs | 3 ++- src/liballoc/raw_vec.rs | 3 ++- src/liballoc/rc.rs | 3 ++- src/liballoc_jemalloc/lib.rs | 4 +--- src/liballoc_system/lib.rs | 11 ++++------- src/libstd/collections/hash/map.rs | 3 ++- src/libstd/collections/hash/table.rs | 3 ++- 9 files changed, 19 insertions(+), 17 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 6a77bf64bae..ccf2e2768d1 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -21,6 +21,7 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; use core::cmp::Ordering; +use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; @@ -31,7 +32,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use heap::{Heap, Alloc, Layout, box_free}; +use heap::{Heap, box_free}; use boxed::Box; use string::String; use vec::Vec; diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fdc3ef4efb8..e59a6e9fdea 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -55,7 +55,7 @@ #![stable(feature = "rust1", since = "1.0.0")] -use heap::{Heap, Layout, Alloc}; +use heap::Heap; use raw_vec::RawVec; use core::any::Any; @@ -63,6 +63,7 @@ use core::borrow; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; +use core::heap::{Alloc, Layout}; use core::iter::FusedIterator; use core::marker::{self, Unpin, Unsize}; use core::mem::{self, Pin}; diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 464f8f2f4ec..49109d522e9 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -41,13 +41,14 @@ // - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. // This implies that even an empty internal node has at least one edge. +use core::heap::{Alloc, Layout}; use core::marker::PhantomData; use core::mem; use core::ptr::{self, Unique, NonNull}; use core::slice; use boxed::Box; -use heap::{Heap, Alloc, Layout}; +use heap::Heap; const B: usize = 6; pub const MIN_LEN: usize = B - 1; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 229ae54d747..3edce8aebdf 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -9,11 +9,12 @@ // except according to those terms. use core::cmp; +use core::heap::{Alloc, Layout}; use core::mem; use core::ops::Drop; use core::ptr::{self, Unique}; use core::slice; -use heap::{Alloc, Layout, Heap}; +use heap::Heap; use super::boxed::Box; use super::allocator::CollectionAllocErr; use super::allocator::CollectionAllocErr::*; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 1fa5d34cb57..8bdc57f96a6 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -250,6 +250,7 @@ use core::cell::Cell; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; +use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::marker; use core::marker::{Unsize, PhantomData}; @@ -259,7 +260,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use heap::{Heap, Alloc, Layout, box_free}; +use heap::{Heap, box_free}; use string::String; use vec::Vec; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index d7370ae400d..7a8d01e4ef8 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -15,7 +15,6 @@ form or name", issue = "27783")] #![deny(warnings)] -#![feature(alloc)] #![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] @@ -25,7 +24,6 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] -extern crate alloc; extern crate alloc_system; extern crate libc; @@ -35,7 +33,7 @@ pub use contents::*; mod contents { use core::ptr; - use alloc::heap::{Alloc, AllocErr, Layout}; + use core::heap::{Alloc, AllocErr, Layout}; use alloc_system::System; use libc::{c_int, c_void, size_t}; diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 1d5e7b73be5..6c1e9cb0b9c 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -17,7 +17,6 @@ issue = "32838")] #![feature(global_allocator)] #![feature(allocator_api)] -#![feature(alloc)] #![feature(core_intrinsics)] #![feature(staged_api)] #![feature(rustc_attrs)] @@ -43,9 +42,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -extern crate alloc; - -use self::alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; +use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; @@ -125,7 +122,7 @@ mod platform { use MIN_ALIGN; use System; - use alloc::heap::{Alloc, AllocErr, Layout}; + use core::heap::{Alloc, AllocErr, Layout}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { @@ -279,7 +276,7 @@ mod platform { use MIN_ALIGN; use System; - use alloc::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace}; + use core::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -491,7 +488,7 @@ mod platform { mod platform { extern crate dlmalloc; - use alloc::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; + use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; use System; use self::dlmalloc::GlobalDlmalloc; diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 474999a6646..452fa4e471c 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,9 +11,10 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::heap::{Heap, Alloc}; +use alloc::heap::Heap; use alloc::allocator::CollectionAllocErr; use cell::Cell; +use core::heap::Alloc; use borrow::Borrow; use cmp::max; use fmt::{self, Debug}; diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 8e78dc546c6..c6861c82a23 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::heap::{Heap, Alloc, Layout}; +use alloc::heap::Heap; +use core::heap::{Alloc, Layout}; use cmp; use hash::{BuildHasher, Hash, Hasher}; -- cgit 1.4.1-3-g733a5 From 1c8d10bce503bdd66921005dbd0b86a31745e5a7 Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 2 Apr 2018 16:33:09 -0700 Subject: Stabilize iter_rfold in 1.27.0 --- src/liballoc/lib.rs | 1 - src/libcore/iter/traits.rs | 4 +--- src/libcore/tests/lib.rs | 1 - 3 files changed, 1 insertion(+), 5 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index e6a311041f5..cbdb135c78c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -100,7 +100,6 @@ #![feature(fundamental)] #![feature(generic_param_attrs)] #![cfg_attr(stage0, feature(i128_type))] -#![feature(iter_rfold)] #![feature(lang_items)] #![feature(needs_allocator)] #![feature(nonzero)] diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index c3aebc4fb23..f84dc98912f 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -491,7 +491,6 @@ pub trait DoubleEndedIterator: Iterator { /// Basic usage: /// /// ``` - /// #![feature(iter_rfold)] /// let a = [1, 2, 3]; /// /// // the sum of all of the elements of a @@ -505,7 +504,6 @@ pub trait DoubleEndedIterator: Iterator { /// and continuing with each element from the back until the front: /// /// ``` - /// #![feature(iter_rfold)] /// let numbers = [1, 2, 3, 4, 5]; /// /// let zero = "0".to_string(); @@ -517,7 +515,7 @@ pub trait DoubleEndedIterator: Iterator { /// assert_eq!(result, "(1 + (2 + (3 + (4 + (5 + 0)))))"); /// ``` #[inline] - #[unstable(feature = "iter_rfold", issue = "44705")] + #[stable(feature = "iter_rfold", since = "1.27.0")] fn rfold(mut self, accum: B, mut f: F) -> B where Self: Sized, F: FnMut(B, Self::Item) -> B, { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 1a68f04532d..e04968a6359 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -29,7 +29,6 @@ #![feature(iterator_flatten)] #![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(iter_rfind)] -#![feature(iter_rfold)] #![feature(iterator_repeat_with)] #![feature(nonzero)] #![feature(pattern)] -- cgit 1.4.1-3-g733a5 From 9b5859aea199d5f34a4d4b5ae7112c5c41f3b242 Mon Sep 17 00:00:00 2001 From: Aidan Hobson Sayers Date: Sun, 18 Feb 2018 17:39:40 +0000 Subject: Remove all unstable placement features Closes #22181, #27779 --- src/grammar/parser-lalr.y | 3 - src/liballoc/binary_heap.rs | 66 +------- src/liballoc/boxed.rs | 151 +---------------- src/liballoc/lib.rs | 12 +- src/liballoc/linked_list.rs | 178 +-------------------- src/liballoc/tests/binary_heap.rs | 20 --- src/liballoc/tests/lib.rs | 2 - src/liballoc/tests/vec.rs | 20 +-- src/liballoc/tests/vec_deque.rs | 22 --- src/liballoc/vec.rs | 76 +-------- src/liballoc/vec_deque.rs | 144 +---------------- src/libcore/ops/mod.rs | 4 - src/libcore/ops/place.rs | 143 ----------------- src/librustc/hir/lowering.rs | 133 --------------- src/librustc/ich/impls_syntax.rs | 1 - src/librustc_lint/unused.rs | 1 - src/librustc_mir/build/matches/test.rs | 7 +- src/librustc_mir/lib.rs | 2 - src/librustc_typeck/diagnostics.rs | 10 -- src/libstd/collections/hash/map.rs | 151 +---------------- src/libstd/collections/hash/table.rs | 26 --- src/libstd/lib.rs | 1 - src/libsyntax/ast.rs | 3 - src/libsyntax/feature_gate.rs | 7 - src/libsyntax/fold.rs | 3 - src/libsyntax/parse/parser.rs | 13 -- src/libsyntax/print/pprust.rs | 13 -- src/libsyntax/util/parser.rs | 14 +- src/libsyntax/visit.rs | 4 - src/libsyntax_pos/hygiene.rs | 2 - src/test/compile-fail/issue-14084.rs | 17 -- src/test/compile-fail/placement-expr-unsafe.rs | 24 --- src/test/compile-fail/placement-expr-unstable.rs | 21 --- src/test/parse-fail/assoc-oddities-1.rs | 3 +- src/test/pretty/stmt_expr_attributes.rs | 1 - src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs | 10 -- .../run-pass-fulldeps/pprust-expr-roundtrip.rs | 39 ++--- src/test/run-pass/new-box-syntax.rs | 10 +- src/test/run-pass/placement-in-syntax.rs | 37 ----- src/test/ui/feature-gate-placement-expr.rs | 26 --- src/test/ui/feature-gate-placement-expr.stderr | 11 -- 41 files changed, 39 insertions(+), 1392 deletions(-) delete mode 100644 src/libcore/ops/place.rs delete mode 100644 src/test/compile-fail/issue-14084.rs delete mode 100644 src/test/compile-fail/placement-expr-unsafe.rs delete mode 100644 src/test/compile-fail/placement-expr-unstable.rs delete mode 100644 src/test/run-pass/placement-in-syntax.rs delete mode 100644 src/test/ui/feature-gate-placement-expr.rs delete mode 100644 src/test/ui/feature-gate-placement-expr.stderr (limited to 'src/liballoc') diff --git a/src/grammar/parser-lalr.y b/src/grammar/parser-lalr.y index de1f96aac50..a7da69f65fa 100644 --- a/src/grammar/parser-lalr.y +++ b/src/grammar/parser-lalr.y @@ -1400,7 +1400,6 @@ nonblock_expr | BREAK lifetime { $$ = mk_node("ExprBreak", 1, $2); } | YIELD { $$ = mk_node("ExprYield", 0); } | YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| nonblock_expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | nonblock_expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | nonblock_expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | nonblock_expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1463,7 +1462,6 @@ expr | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } | YIELD { $$ = mk_node("ExprYield", 0); } | YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| expr LARROW expr { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr '=' expr { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr SHLEQ expr { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | expr SHREQ expr { $$ = mk_node("ExprAssignShr", 2, $1, $3); } @@ -1527,7 +1525,6 @@ expr_nostruct | BREAK ident { $$ = mk_node("ExprBreak", 1, $2); } | YIELD { $$ = mk_node("ExprYield", 0); } | YIELD expr { $$ = mk_node("ExprYield", 1, $2); } -| expr_nostruct LARROW expr_nostruct { $$ = mk_node("ExprInPlace", 2, $1, $3); } | expr_nostruct '=' expr_nostruct { $$ = mk_node("ExprAssign", 2, $1, $3); } | expr_nostruct SHLEQ expr_nostruct { $$ = mk_node("ExprAssignShl", 2, $1, $3); } | expr_nostruct SHREQ expr_nostruct { $$ = mk_node("ExprAssignShr", 2, $1, $3); } diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index f6a666b599b..668b61c51d8 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -155,7 +155,7 @@ #![allow(missing_docs)] #![stable(feature = "rust1", since = "1.0.0")] -use core::ops::{Deref, DerefMut, Place, Placer, InPlace}; +use core::ops::{Deref, DerefMut}; use core::iter::{FromIterator, FusedIterator}; use core::mem::{swap, size_of}; use core::ptr; @@ -1195,67 +1195,3 @@ impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { self.extend(iter.into_iter().cloned()); } } - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -pub struct BinaryHeapPlace<'a, T: 'a> -where T: Clone + Ord { - heap: *mut BinaryHeap, - place: vec::PlaceBack<'a, T>, -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T: Clone + Ord + fmt::Debug> fmt::Debug for BinaryHeapPlace<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("BinaryHeapPlace") - .field(&self.place) - .finish() - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T: 'a> Placer for &'a mut BinaryHeap -where T: Clone + Ord { - type Place = BinaryHeapPlace<'a, T>; - - fn make_place(self) -> Self::Place { - let ptr = self as *mut BinaryHeap; - let place = Placer::make_place(self.data.place_back()); - BinaryHeapPlace { - heap: ptr, - place, - } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for BinaryHeapPlace<'a, T> -where T: Clone + Ord { - fn pointer(&mut self) -> *mut T { - self.place.pointer() - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for BinaryHeapPlace<'a, T> -where T: Clone + Ord { - type Owner = &'a T; - - unsafe fn finalize(self) -> &'a T { - self.place.finalize(); - - let heap: &mut BinaryHeap = &mut *self.heap; - let len = heap.len(); - let i = heap.sift_up(0, len - 1); - heap.data.get_unchecked(i) - } -} diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index e59a6e9fdea..4f9dc61ce19 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -55,7 +55,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -use heap::Heap; use raw_vec::RawVec; use core::any::Any; @@ -63,47 +62,14 @@ use core::borrow; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::heap::{Alloc, Layout}; use core::iter::FusedIterator; -use core::marker::{self, Unpin, Unsize}; +use core::marker::{Unpin, Unsize}; use core::mem::{self, Pin}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; -use core::ops::{BoxPlace, Boxed, InPlace, Place, Placer}; use core::ptr::{self, NonNull, Unique}; use core::convert::From; use str::from_boxed_utf8_unchecked; -/// A value that represents the heap. This is the default place that the `box` -/// keyword allocates into when no place is supplied. -/// -/// The following two examples are equivalent: -/// -/// ``` -/// #![feature(box_heap)] -/// -/// #![feature(box_syntax, placement_in_syntax)] -/// use std::boxed::HEAP; -/// -/// fn main() { -/// let foo: Box = in HEAP { 5 }; -/// let foo = box 5; -/// } -/// ``` -#[unstable(feature = "box_heap", - reason = "may be renamed; uncertain about custom allocator design", - issue = "27779")] -pub const HEAP: ExchangeHeapSingleton = ExchangeHeapSingleton { _force_singleton: () }; - -/// This the singleton type used solely for `boxed::HEAP`. -#[unstable(feature = "box_heap", - reason = "may be renamed; uncertain about custom allocator design", - issue = "27779")] -#[allow(missing_debug_implementations)] -#[derive(Copy, Clone)] -pub struct ExchangeHeapSingleton { - _force_singleton: (), -} - /// A pointer type for heap allocation. /// /// See the [module-level documentation](../../std/boxed/index.html) for more. @@ -112,121 +78,6 @@ pub struct ExchangeHeapSingleton { #[stable(feature = "rust1", since = "1.0.0")] pub struct Box(Unique); -/// `IntermediateBox` represents uninitialized backing storage for `Box`. -/// -/// FIXME (pnkfelix): Ideally we would just reuse `Box` instead of -/// introducing a separate `IntermediateBox`; but then you hit -/// issues when you e.g. attempt to destructure an instance of `Box`, -/// since it is a lang item and so it gets special handling by the -/// compiler. Easier just to make this parallel type for now. -/// -/// FIXME (pnkfelix): Currently the `box` protocol only supports -/// creating instances of sized types. This IntermediateBox is -/// designed to be forward-compatible with a future protocol that -/// supports creating instances of unsized types; that is why the type -/// parameter has the `?Sized` generalization marker, and is also why -/// this carries an explicit size. However, it probably does not need -/// to carry the explicit alignment; that is just a work-around for -/// the fact that the `align_of` intrinsic currently requires the -/// input type to be Sized (which I do not think is strictly -/// necessary). -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -#[allow(missing_debug_implementations)] -pub struct IntermediateBox { - ptr: *mut u8, - layout: Layout, - marker: marker::PhantomData<*mut T>, -} - -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -unsafe impl Place for IntermediateBox { - fn pointer(&mut self) -> *mut T { - self.ptr as *mut T - } -} - -unsafe fn finalize(b: IntermediateBox) -> Box { - let p = b.ptr as *mut T; - mem::forget(b); - Box::from_raw(p) -} - -fn make_place() -> IntermediateBox { - let layout = Layout::new::(); - - let p = if layout.size() == 0 { - mem::align_of::() as *mut u8 - } else { - unsafe { - Heap.alloc(layout.clone()).unwrap_or_else(|err| { - Heap.oom(err) - }) - } - }; - - IntermediateBox { - ptr: p, - layout, - marker: marker::PhantomData, - } -} - -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -impl BoxPlace for IntermediateBox { - fn make_place() -> IntermediateBox { - make_place() - } -} - -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -impl InPlace for IntermediateBox { - type Owner = Box; - unsafe fn finalize(self) -> Box { - finalize(self) - } -} - -#[unstable(feature = "placement_new_protocol", issue = "27779")] -impl Boxed for Box { - type Data = T; - type Place = IntermediateBox; - unsafe fn finalize(b: IntermediateBox) -> Box { - finalize(b) - } -} - -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -impl Placer for ExchangeHeapSingleton { - type Place = IntermediateBox; - - fn make_place(self) -> IntermediateBox { - make_place() - } -} - -#[unstable(feature = "placement_in", - reason = "placement box design is still being worked out.", - issue = "27779")] -impl Drop for IntermediateBox { - fn drop(&mut self) { - if self.layout.size() > 0 { - unsafe { - Heap.dealloc(self.ptr, self.layout.clone()) - } - } - } -} - impl Box { /// Allocates memory on the heap and then places `x` into it. /// diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index e6a311041f5..2fad3b0bad4 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -76,7 +76,6 @@ #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(test, feature(placement_in))] #![cfg_attr(not(test), feature(core_float))] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] @@ -108,8 +107,6 @@ #![feature(optin_builtin_traits)] #![feature(pattern)] #![feature(pin)] -#![feature(placement_in_syntax)] -#![feature(placement_new_protocol)] #![feature(ptr_internals)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] @@ -128,8 +125,8 @@ #![feature(pointer_methods)] #![feature(inclusive_range_fields)] -#![cfg_attr(not(test), feature(fn_traits, placement_new_protocol, swap_with_slice, i128))] -#![cfg_attr(test, feature(test, box_heap))] +#![cfg_attr(not(test), feature(fn_traits, swap_with_slice, i128))] +#![cfg_attr(test, feature(test))] // Allow testing this library @@ -159,13 +156,12 @@ pub mod heap; // Need to conditionally define the mod from `boxed.rs` to avoid // duplicating the lang-items when building in test cfg; but also need -// to allow code to have `use boxed::HEAP;` -// and `use boxed::Box;` declarations. +// to allow code to have `use boxed::Box;` declarations. #[cfg(not(test))] pub mod boxed; #[cfg(test)] mod boxed { - pub use std::boxed::{Box, IntermediateBox, HEAP}; + pub use std::boxed::Box; } #[cfg(test)] mod boxed_test; diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 097d2e414f5..129b3bc6764 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -28,10 +28,9 @@ use core::hash::{Hasher, Hash}; use core::iter::{FromIterator, FusedIterator}; use core::marker::PhantomData; use core::mem; -use core::ops::{BoxPlace, InPlace, Place, Placer}; -use core::ptr::{self, NonNull}; +use core::ptr::NonNull; -use boxed::{Box, IntermediateBox}; +use boxed::Box; use super::SpecExtend; /// A doubly-linked list with owned nodes. @@ -786,62 +785,6 @@ impl LinkedList { old_len: old_len, } } - - /// Returns a place for insertion at the front of the list. - /// - /// Using this method with placement syntax is equivalent to - /// [`push_front`](#method.push_front), but may be more efficient. - /// - /// # Examples - /// - /// ``` - /// #![feature(collection_placement)] - /// #![feature(placement_in_syntax)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list = LinkedList::new(); - /// list.front_place() <- 2; - /// list.front_place() <- 4; - /// assert!(list.iter().eq(&[4, 2])); - /// ``` - #[unstable(feature = "collection_placement", - reason = "method name and placement protocol are subject to change", - issue = "30172")] - pub fn front_place(&mut self) -> FrontPlace { - FrontPlace { - list: self, - node: IntermediateBox::make_place(), - } - } - - /// Returns a place for insertion at the back of the list. - /// - /// Using this method with placement syntax is equivalent to [`push_back`](#method.push_back), - /// but may be more efficient. - /// - /// # Examples - /// - /// ``` - /// #![feature(collection_placement)] - /// #![feature(placement_in_syntax)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list = LinkedList::new(); - /// list.back_place() <- 2; - /// list.back_place() <- 4; - /// assert!(list.iter().eq(&[2, 4])); - /// ``` - #[unstable(feature = "collection_placement", - reason = "method name and placement protocol are subject to change", - issue = "30172")] - pub fn back_place(&mut self) -> BackPlace { - BackPlace { - list: self, - node: IntermediateBox::make_place(), - } - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -1242,123 +1185,6 @@ impl Hash for LinkedList { } } -unsafe fn finalize(node: IntermediateBox>) -> Box> { - let mut node = node.finalize(); - ptr::write(&mut node.next, None); - ptr::write(&mut node.prev, None); - node -} - -/// A place for insertion at the front of a `LinkedList`. -/// -/// See [`LinkedList::front_place`](struct.LinkedList.html#method.front_place) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -pub struct FrontPlace<'a, T: 'a> { - list: &'a mut LinkedList, - node: IntermediateBox>, -} - -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for FrontPlace<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("FrontPlace") - .field(&self.list) - .finish() - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> Placer for FrontPlace<'a, T> { - type Place = Self; - - fn make_place(self) -> Self { - self - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for FrontPlace<'a, T> { - fn pointer(&mut self) -> *mut T { - unsafe { &mut (*self.node.pointer()).element } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for FrontPlace<'a, T> { - type Owner = (); - - unsafe fn finalize(self) { - let FrontPlace { list, node } = self; - list.push_front_node(finalize(node)); - } -} - -/// A place for insertion at the back of a `LinkedList`. -/// -/// See [`LinkedList::back_place`](struct.LinkedList.html#method.back_place) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -pub struct BackPlace<'a, T: 'a> { - list: &'a mut LinkedList, - node: IntermediateBox>, -} - -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for BackPlace<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("BackPlace") - .field(&self.list) - .finish() - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> Placer for BackPlace<'a, T> { - type Place = Self; - - fn make_place(self) -> Self { - self - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for BackPlace<'a, T> { - fn pointer(&mut self) -> *mut T { - unsafe { &mut (*self.node.pointer()).element } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for BackPlace<'a, T> { - type Owner = (); - - unsafe fn finalize(self) { - let BackPlace { list, node } = self; - list.push_back_node(finalize(node)); - } -} - // Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. #[allow(dead_code)] fn assert_covariance() { diff --git a/src/liballoc/tests/binary_heap.rs b/src/liballoc/tests/binary_heap.rs index 5c979d82e55..8494463463c 100644 --- a/src/liballoc/tests/binary_heap.rs +++ b/src/liballoc/tests/binary_heap.rs @@ -278,26 +278,6 @@ fn test_extend_specialization() { assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); } -#[test] -fn test_placement() { - let mut a = BinaryHeap::new(); - &mut a <- 2; - &mut a <- 4; - &mut a <- 3; - assert_eq!(a.peek(), Some(&4)); - assert_eq!(a.len(), 3); - &mut a <- 1; - assert_eq!(a.into_sorted_vec(), vec![1, 2, 3, 4]); -} - -#[test] -fn test_placement_panic() { - let mut heap = BinaryHeap::from(vec![1, 2, 3]); - fn mkpanic() -> usize { panic!() } - let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { &mut heap <- mkpanic(); })); - assert_eq!(heap.len(), 3); -} - #[allow(dead_code)] fn assert_covariance() { fn drain<'new>(d: Drain<'static, &'static str>) -> Drain<'new, &'new str> { diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 0a7e9a8be94..1a49fb9964a 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -15,13 +15,11 @@ #![feature(attr_literals)] #![feature(box_syntax)] #![cfg_attr(stage0, feature(inclusive_range_syntax))] -#![feature(collection_placement)] #![feature(const_fn)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] #![feature(iterator_step_by)] #![feature(pattern)] -#![feature(placement_in_syntax)] #![feature(rand)] #![feature(slice_sort_by_cached_key)] #![feature(splice)] diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 3c17a401bba..2895c53009d 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -10,7 +10,7 @@ use std::borrow::Cow; use std::mem::size_of; -use std::{usize, isize, panic}; +use std::{usize, isize}; use std::vec::{Drain, IntoIter}; use std::collections::CollectionAllocErr::*; @@ -753,24 +753,6 @@ fn assert_covariance() { } } -#[test] -fn test_placement() { - let mut vec = vec![1]; - assert_eq!(vec.place_back() <- 2, &2); - assert_eq!(vec.len(), 2); - assert_eq!(vec.place_back() <- 3, &3); - assert_eq!(vec.len(), 3); - assert_eq!(&vec, &[1, 2, 3]); -} - -#[test] -fn test_placement_panic() { - let mut vec = vec![1, 2, 3]; - fn mkpanic() -> usize { panic!() } - let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { vec.place_back() <- mkpanic(); })); - assert_eq!(vec.len(), 3); -} - #[test] fn from_into_inner() { let vec = vec![1, 2, 3]; diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index fc1a0b624a5..75d3f01f8b6 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -1004,28 +1004,6 @@ fn test_is_empty() { assert!(v.into_iter().is_empty()); } -#[test] -fn test_placement_in() { - let mut buf: VecDeque = VecDeque::new(); - buf.place_back() <- 1; - buf.place_back() <- 2; - assert_eq!(buf, [1,2]); - - buf.place_front() <- 3; - buf.place_front() <- 4; - assert_eq!(buf, [4,3,1,2]); - - { - let ptr_head = buf.place_front() <- 5; - assert_eq!(*ptr_head, 5); - } - { - let ptr_tail = buf.place_back() <- 6; - assert_eq!(*ptr_tail, 6); - } - assert_eq!(buf, [5,4,3,1,2,6]); -} - #[test] fn test_reserve_exact_2() { // This is all the same as test_reserve diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2eedb964f88..47c92028b14 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -76,7 +76,7 @@ use core::mem; #[cfg(not(test))] use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{InPlace, Index, IndexMut, Place, Placer, RangeBounds}; +use core::ops::{Index, IndexMut, RangeBounds}; use core::ops; use core::ptr; use core::ptr::NonNull; @@ -1065,29 +1065,6 @@ impl Vec { } } - /// Returns a place for insertion at the back of the `Vec`. - /// - /// Using this method with placement syntax is equivalent to [`push`](#method.push), - /// but may be more efficient. - /// - /// # Examples - /// - /// ``` - /// #![feature(collection_placement)] - /// #![feature(placement_in_syntax)] - /// - /// let mut vec = vec![1, 2]; - /// vec.place_back() <- 3; - /// vec.place_back() <- 4; - /// assert_eq!(&vec, &[1, 2, 3, 4]); - /// ``` - #[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] - pub fn place_back(&mut self) -> PlaceBack { - PlaceBack { vec: self } - } - /// Removes the last element from a vector and returns it, or [`None`] if it /// is empty. /// @@ -2492,57 +2469,6 @@ impl<'a, T> ExactSizeIterator for Drain<'a, T> { #[stable(feature = "fused", since = "1.26.0")] impl<'a, T> FusedIterator for Drain<'a, T> {} -/// A place for insertion at the back of a `Vec`. -/// -/// See [`Vec::place_back`](struct.Vec.html#method.place_back) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -#[derive(Debug)] -pub struct PlaceBack<'a, T: 'a> { - vec: &'a mut Vec, -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> Placer for PlaceBack<'a, T> { - type Place = PlaceBack<'a, T>; - - fn make_place(self) -> Self { - // This will panic or abort if we would allocate > isize::MAX bytes - // or if the length increment would overflow for zero-sized types. - if self.vec.len == self.vec.buf.cap() { - self.vec.buf.double(); - } - self - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for PlaceBack<'a, T> { - fn pointer(&mut self) -> *mut T { - unsafe { self.vec.as_mut_ptr().offset(self.vec.len as isize) } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for PlaceBack<'a, T> { - type Owner = &'a mut T; - - unsafe fn finalize(mut self) -> &'a mut T { - let ptr = self.pointer(); - self.vec.len += 1; - &mut *ptr - } -} - - /// A splicing iterator for `Vec`. /// /// This struct is created by the [`splice()`] method on [`Vec`]. See its diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 94d042a45aa..f28c8e38996 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -22,7 +22,7 @@ use core::fmt; use core::iter::{repeat, FromIterator, FusedIterator}; use core::mem; use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Index, IndexMut, Place, Placer, InPlace, RangeBounds}; +use core::ops::{Index, IndexMut, RangeBounds}; use core::ptr; use core::ptr::NonNull; use core::slice; @@ -1885,56 +1885,6 @@ impl VecDeque { debug_assert!(!self.is_full()); } } - - /// Returns a place for insertion at the back of the `VecDeque`. - /// - /// Using this method with placement syntax is equivalent to [`push_back`](#method.push_back), - /// but may be more efficient. - /// - /// # Examples - /// - /// ``` - /// #![feature(collection_placement)] - /// #![feature(placement_in_syntax)] - /// - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.place_back() <- 3; - /// buf.place_back() <- 4; - /// assert_eq!(&buf, &[3, 4]); - /// ``` - #[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] - pub fn place_back(&mut self) -> PlaceBack { - PlaceBack { vec_deque: self } - } - - /// Returns a place for insertion at the front of the `VecDeque`. - /// - /// Using this method with placement syntax is equivalent to [`push_front`](#method.push_front), - /// but may be more efficient. - /// - /// # Examples - /// - /// ``` - /// #![feature(collection_placement)] - /// #![feature(placement_in_syntax)] - /// - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.place_front() <- 3; - /// buf.place_front() <- 4; - /// assert_eq!(&buf, &[4, 3]); - /// ``` - #[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] - pub fn place_front(&mut self) -> PlaceFront { - PlaceFront { vec_deque: self } - } } impl VecDeque { @@ -2662,98 +2612,6 @@ impl From> for Vec { } } -/// A place for insertion at the back of a `VecDeque`. -/// -/// See [`VecDeque::place_back`](struct.VecDeque.html#method.place_back) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -#[derive(Debug)] -pub struct PlaceBack<'a, T: 'a> { - vec_deque: &'a mut VecDeque, -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> Placer for PlaceBack<'a, T> { - type Place = PlaceBack<'a, T>; - - fn make_place(self) -> Self { - self.vec_deque.grow_if_necessary(); - self - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for PlaceBack<'a, T> { - fn pointer(&mut self) -> *mut T { - unsafe { self.vec_deque.ptr().offset(self.vec_deque.head as isize) } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for PlaceBack<'a, T> { - type Owner = &'a mut T; - - unsafe fn finalize(self) -> &'a mut T { - let head = self.vec_deque.head; - self.vec_deque.head = self.vec_deque.wrap_add(head, 1); - &mut *(self.vec_deque.ptr().offset(head as isize)) - } -} - -/// A place for insertion at the front of a `VecDeque`. -/// -/// See [`VecDeque::place_front`](struct.VecDeque.html#method.place_front) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol are subject to change", - issue = "30172")] -#[derive(Debug)] -pub struct PlaceFront<'a, T: 'a> { - vec_deque: &'a mut VecDeque, -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> Placer for PlaceFront<'a, T> { - type Place = PlaceFront<'a, T>; - - fn make_place(self) -> Self { - self.vec_deque.grow_if_necessary(); - self - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, T> Place for PlaceFront<'a, T> { - fn pointer(&mut self) -> *mut T { - let tail = self.vec_deque.wrap_sub(self.vec_deque.tail, 1); - unsafe { self.vec_deque.ptr().offset(tail as isize) } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, T> InPlace for PlaceFront<'a, T> { - type Owner = &'a mut T; - - unsafe fn finalize(self) -> &'a mut T { - self.vec_deque.tail = self.vec_deque.wrap_sub(self.vec_deque.tail, 1); - &mut *(self.vec_deque.ptr().offset(self.vec_deque.tail as isize)) - } -} - #[cfg(test)] mod tests { use test; diff --git a/src/libcore/ops/mod.rs b/src/libcore/ops/mod.rs index 0b480b618fc..ce4f45762de 100644 --- a/src/libcore/ops/mod.rs +++ b/src/libcore/ops/mod.rs @@ -161,7 +161,6 @@ mod drop; mod function; mod generator; mod index; -mod place; mod range; mod try; mod unsize; @@ -200,8 +199,5 @@ pub use self::try::Try; #[unstable(feature = "generator_trait", issue = "43122")] pub use self::generator::{Generator, GeneratorState}; -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub use self::place::{Place, Placer, InPlace, Boxed, BoxPlace}; - #[unstable(feature = "coerce_unsized", issue = "27732")] pub use self::unsize::CoerceUnsized; diff --git a/src/libcore/ops/place.rs b/src/libcore/ops/place.rs deleted file mode 100644 index b3dcf4e7ee9..00000000000 --- a/src/libcore/ops/place.rs +++ /dev/null @@ -1,143 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -/// Both `PLACE <- EXPR` and `box EXPR` desugar into expressions -/// that allocate an intermediate "place" that holds uninitialized -/// state. The desugaring evaluates EXPR, and writes the result at -/// the address returned by the `pointer` method of this trait. -/// -/// A `Place` can be thought of as a special representation for a -/// hypothetical `&uninit` reference (which Rust cannot currently -/// express directly). That is, it represents a pointer to -/// uninitialized storage. -/// -/// The client is responsible for two steps: First, initializing the -/// payload (it can access its address via `pointer`). Second, -/// converting the agent to an instance of the owning pointer, via the -/// appropriate `finalize` method (see the `InPlace`. -/// -/// If evaluating EXPR fails, then it is up to the destructor for the -/// implementation of Place to clean up any intermediate state -/// (e.g. deallocate box storage, pop a stack, etc). -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub unsafe trait Place { - /// Returns the address where the input value will be written. - /// Note that the data at this address is generally uninitialized, - /// and thus one should use `ptr::write` for initializing it. - /// - /// This function must return a pointer through which a value - /// of type `Data` can be written. - fn pointer(&mut self) -> *mut Data; -} - -/// Interface to implementations of `PLACE <- EXPR`. -/// -/// `PLACE <- EXPR` effectively desugars into: -/// -/// ``` -/// # #![feature(placement_new_protocol, box_heap)] -/// # use std::ops::{Placer, Place, InPlace}; -/// # #[allow(non_snake_case)] -/// # fn main() { -/// # let PLACE = std::boxed::HEAP; -/// # let EXPR = 1; -/// let p = PLACE; -/// let mut place = Placer::make_place(p); -/// let raw_place = Place::pointer(&mut place); -/// let value = EXPR; -/// unsafe { -/// std::ptr::write(raw_place, value); -/// InPlace::finalize(place) -/// } -/// # ; } -/// ``` -/// -/// The type of `PLACE <- EXPR` is derived from the type of `PLACE`; -/// if the type of `PLACE` is `P`, then the final type of the whole -/// expression is `P::Place::Owner` (see the `InPlace` and `Boxed` -/// traits). -/// -/// Values for types implementing this trait usually are transient -/// intermediate values (e.g. the return value of `Vec::emplace_back`) -/// or `Copy`, since the `make_place` method takes `self` by value. -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub trait Placer { - /// `Place` is the intermediate agent guarding the - /// uninitialized state for `Data`. - type Place: InPlace; - - /// Creates a fresh place from `self`. - fn make_place(self) -> Self::Place; -} - -/// Specialization of `Place` trait supporting `PLACE <- EXPR`. -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub trait InPlace: Place { - /// `Owner` is the type of the end value of `PLACE <- EXPR` - /// - /// Note that when `PLACE <- EXPR` is solely used for - /// side-effecting an existing data-structure, - /// e.g. `Vec::emplace_back`, then `Owner` need not carry any - /// information at all (e.g. it can be the unit type `()` in that - /// case). - type Owner; - - /// Converts self into the final value, shifting - /// deallocation/cleanup responsibilities (if any remain), over to - /// the returned instance of `Owner` and forgetting self. - unsafe fn finalize(self) -> Self::Owner; -} - -/// Core trait for the `box EXPR` form. -/// -/// `box EXPR` effectively desugars into: -/// -/// ``` -/// # #![feature(placement_new_protocol)] -/// # use std::ops::{BoxPlace, Place, Boxed}; -/// # #[allow(non_snake_case)] -/// # fn main() { -/// # let EXPR = 1; -/// let mut place = BoxPlace::make_place(); -/// let raw_place = Place::pointer(&mut place); -/// let value = EXPR; -/// # let _: Box<_> = -/// unsafe { -/// ::std::ptr::write(raw_place, value); -/// Boxed::finalize(place) -/// } -/// # ; } -/// ``` -/// -/// The type of `box EXPR` is supplied from its surrounding -/// context; in the above expansion, the result type `T` is used -/// to determine which implementation of `Boxed` to use, and that -/// `` in turn dictates determines which -/// implementation of `BoxPlace` to use, namely: -/// `<::Place as BoxPlace>`. -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub trait Boxed { - /// The kind of data that is stored in this kind of box. - type Data; /* (`Data` unused b/c cannot yet express below bound.) */ - /// The place that will negotiate the storage of the data. - type Place: BoxPlace; - - /// Converts filled place into final owning value, shifting - /// deallocation/cleanup responsibilities (if any remain), over to - /// returned instance of `Self` and forgetting `filled`. - unsafe fn finalize(filled: Self::Place) -> Self; -} - -/// Specialization of `Place` trait supporting `box EXPR`. -#[unstable(feature = "placement_new_protocol", issue = "27779")] -pub trait BoxPlace : Place { - /// Creates a globally fresh place. - fn make_place() -> Self; -} diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index 536d682566a..5f9f37094f5 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -2911,118 +2911,8 @@ impl<'a> LoweringContext<'a> { fn lower_expr(&mut self, e: &Expr) -> hir::Expr { let kind = match e.node { - // Issue #22181: - // Eventually a desugaring for `box EXPR` - // (similar to the desugaring above for `in PLACE BLOCK`) - // should go here, desugaring - // - // to: - // - // let mut place = BoxPlace::make_place(); - // let raw_place = Place::pointer(&mut place); - // let value = $value; - // unsafe { - // ::std::ptr::write(raw_place, value); - // Boxed::finalize(place) - // } - // - // But for now there are type-inference issues doing that. ExprKind::Box(ref inner) => hir::ExprBox(P(self.lower_expr(inner))), - // Desugar ExprBox: `in (PLACE) EXPR` - ExprKind::InPlace(ref placer, ref value_expr) => { - // to: - // - // let p = PLACE; - // let mut place = Placer::make_place(p); - // let raw_place = Place::pointer(&mut place); - // push_unsafe!({ - // std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR )); - // InPlace::finalize(place) - // }) - let placer_expr = P(self.lower_expr(placer)); - let value_expr = P(self.lower_expr(value_expr)); - - let placer_ident = self.str_to_ident("placer"); - let place_ident = self.str_to_ident("place"); - let p_ptr_ident = self.str_to_ident("p_ptr"); - - let make_place = ["ops", "Placer", "make_place"]; - let place_pointer = ["ops", "Place", "pointer"]; - let move_val_init = ["intrinsics", "move_val_init"]; - let inplace_finalize = ["ops", "InPlace", "finalize"]; - - let unstable_span = - self.allow_internal_unstable(CompilerDesugaringKind::BackArrow, e.span); - let make_call = |this: &mut LoweringContext, p, args| { - let path = P(this.expr_std_path(unstable_span, p, ThinVec::new())); - P(this.expr_call(e.span, path, args)) - }; - - let mk_stmt_let = |this: &mut LoweringContext, bind, expr| { - this.stmt_let(e.span, false, bind, expr) - }; - - let mk_stmt_let_mut = |this: &mut LoweringContext, bind, expr| { - this.stmt_let(e.span, true, bind, expr) - }; - - // let placer = ; - let (s1, placer_binding) = { mk_stmt_let(self, placer_ident, placer_expr) }; - - // let mut place = Placer::make_place(placer); - let (s2, place_binding) = { - let placer = self.expr_ident(e.span, placer_ident, placer_binding); - let call = make_call(self, &make_place, hir_vec![placer]); - mk_stmt_let_mut(self, place_ident, call) - }; - - // let p_ptr = Place::pointer(&mut place); - let (s3, p_ptr_binding) = { - let agent = P(self.expr_ident(e.span, place_ident, place_binding)); - let args = hir_vec![self.expr_mut_addr_of(e.span, agent)]; - let call = make_call(self, &place_pointer, args); - mk_stmt_let(self, p_ptr_ident, call) - }; - - // pop_unsafe!(EXPR)); - let pop_unsafe_expr = { - self.signal_block_expr( - hir_vec![], - value_expr, - e.span, - hir::PopUnsafeBlock(hir::CompilerGenerated), - ThinVec::new(), - ) - }; - - // push_unsafe!({ - // std::intrinsics::move_val_init(raw_place, pop_unsafe!( EXPR )); - // InPlace::finalize(place) - // }) - let expr = { - let ptr = self.expr_ident(e.span, p_ptr_ident, p_ptr_binding); - let call_move_val_init = hir::StmtSemi( - make_call(self, &move_val_init, hir_vec![ptr, pop_unsafe_expr]), - self.next_id().node_id, - ); - let call_move_val_init = respan(e.span, call_move_val_init); - - let place = self.expr_ident(e.span, place_ident, place_binding); - let call = make_call(self, &inplace_finalize, hir_vec![place]); - P(self.signal_block_expr( - hir_vec![call_move_val_init], - call, - e.span, - hir::PushUnsafeBlock(hir::CompilerGenerated), - ThinVec::new(), - )) - }; - - let block = self.block_all(e.span, hir_vec![s1, s2, s3], Some(expr)); - hir::ExprBlock(P(block)) - } - ExprKind::Array(ref exprs) => { hir::ExprArray(exprs.iter().map(|x| self.lower_expr(x)).collect()) } @@ -4069,29 +3959,6 @@ impl<'a> LoweringContext<'a> { .resolve_str_path(span, self.crate_root, components, is_value) } - fn signal_block_expr( - &mut self, - stmts: hir::HirVec, - expr: P, - span: Span, - rule: hir::BlockCheckMode, - attrs: ThinVec, - ) -> hir::Expr { - let LoweredNodeId { node_id, hir_id } = self.next_id(); - - let block = P(hir::Block { - rules: rule, - span, - id: node_id, - hir_id, - stmts, - expr: Some(expr), - targeted_by_break: false, - recovered: false, - }); - self.expr_block(block, attrs) - } - fn ty_path(&mut self, id: LoweredNodeId, span: Span, qpath: hir::QPath) -> P { let mut id = id; let node = match qpath { diff --git a/src/librustc/ich/impls_syntax.rs b/src/librustc/ich/impls_syntax.rs index 0b037964981..425459f448f 100644 --- a/src/librustc/ich/impls_syntax.rs +++ b/src/librustc/ich/impls_syntax.rs @@ -371,7 +371,6 @@ impl_stable_hash_for!(enum ::syntax_pos::hygiene::ExpnFormat { }); impl_stable_hash_for!(enum ::syntax_pos::hygiene::CompilerDesugaringKind { - BackArrow, DotFill, QuestionMark }); diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index 86f79c553c3..aa93b3098e0 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -301,7 +301,6 @@ impl EarlyLintPass for UnusedParens { Ret(Some(ref value)) => (value, "`return` value", false), Assign(_, ref value) => (value, "assigned value", false), AssignOp(.., ref value) => (value, "assigned value", false), - InPlace(_, ref value) => (value, "emplacement value", false), // either function/method call, or something this lint doesn't care about ref call_or_other => { let args_to_check; diff --git a/src/librustc_mir/build/matches/test.rs b/src/librustc_mir/build/matches/test.rs index 09579eaecb2..c50d84c10d8 100644 --- a/src/librustc_mir/build/matches/test.rs +++ b/src/librustc_mir/build/matches/test.rs @@ -196,15 +196,16 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let mut values = Vec::with_capacity(used_variants); let tcx = self.hir.tcx(); for (idx, discr) in adt_def.discriminants(tcx).enumerate() { - target_blocks.place_back() <- if variants.contains(idx) { + target_blocks.push(if variants.contains(idx) { values.push(discr.val); - *(targets.place_back() <- self.cfg.start_new_block()) + targets.push(self.cfg.start_new_block()); + *targets.last().unwrap() } else { if otherwise_block.is_none() { otherwise_block = Some(self.cfg.start_new_block()); } otherwise_block.unwrap() - }; + }); } if let Some(otherwise_block) = otherwise_block { targets.push(otherwise_block); diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 7af3a397666..84baa8c5417 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -34,8 +34,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(exhaustive_patterns)] #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] -#![feature(placement_in_syntax)] -#![feature(collection_placement)] #![feature(nonzero)] #![cfg_attr(stage0, feature(underscore_lifetimes))] #![cfg_attr(stage0, feature(never_type))] diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 1f882676f61..ca5858299c5 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -721,16 +721,6 @@ fn main() { ``` "##, -E0066: r##" -Box placement expressions (like C++'s "placement new") do not yet support any -place expression except the exchange heap (i.e. `std::boxed::HEAP`). -Furthermore, the syntax is changing to use `in` instead of `box`. See [RFC 470] -and [RFC 809] for more details. - -[RFC 470]: https://github.com/rust-lang/rfcs/pull/470 -[RFC 809]: https://github.com/rust-lang/rfcs/blob/master/text/0809-box-and-in-for-stdlib.md -"##, - E0067: r##" The left-hand side of a compound assignment expression must be a place expression. A place expression represents a memory location and includes diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 452fa4e471c..e0b48e565d0 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -22,8 +22,7 @@ use fmt::{self, Debug}; use hash::{Hash, Hasher, BuildHasher, SipHasher13}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; -use ops::{Deref, Index, InPlace, Place, Placer}; -use ptr; +use ops::{Deref, Index}; use sys; use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; @@ -2043,80 +2042,6 @@ impl<'a, K, V> fmt::Debug for Drain<'a, K, V> } } -/// A place for insertion to a `Entry`. -/// -/// See [`HashMap::entry`](struct.HashMap.html#method.entry) for details. -#[must_use = "places do nothing unless written to with `<-` syntax"] -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol is subject to change", - issue = "30172")] -pub struct EntryPlace<'a, K: 'a, V: 'a> { - bucket: FullBucketMut<'a, K, V>, -} - -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol is subject to change", - issue = "30172")] -impl<'a, K: 'a + Debug, V: 'a + Debug> Debug for EntryPlace<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("EntryPlace") - .field("key", self.bucket.read().0) - .field("value", self.bucket.read().1) - .finish() - } -} - -#[unstable(feature = "collection_placement", - reason = "struct name and placement protocol is subject to change", - issue = "30172")] -impl<'a, K, V> Drop for EntryPlace<'a, K, V> { - fn drop(&mut self) { - // Inplacement insertion failed. Only key need to drop. - // The value is failed to insert into map. - unsafe { self.bucket.remove_key() }; - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, K, V> Placer for Entry<'a, K, V> { - type Place = EntryPlace<'a, K, V>; - - fn make_place(self) -> EntryPlace<'a, K, V> { - let b = match self { - Occupied(mut o) => { - unsafe { ptr::drop_in_place(o.elem.read_mut().1); } - o.elem - } - Vacant(v) => { - unsafe { v.insert_key() } - } - }; - EntryPlace { bucket: b } - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -unsafe impl<'a, K, V> Place for EntryPlace<'a, K, V> { - fn pointer(&mut self) -> *mut V { - self.bucket.read_mut().1 - } -} - -#[unstable(feature = "collection_placement", - reason = "placement protocol is subject to change", - issue = "30172")] -impl<'a, K, V> InPlace for EntryPlace<'a, K, V> { - type Owner = (); - - unsafe fn finalize(self) { - mem::forget(self); - } -} - impl<'a, K, V> Entry<'a, K, V> { #[stable(feature = "rust1", since = "1.0.0")] /// Ensures a value is in the entry by inserting the default if empty, and returns @@ -2539,26 +2464,6 @@ impl<'a, K: 'a, V: 'a> VacantEntry<'a, K, V> { }; b.into_mut_refs().1 } - - // Only used for InPlacement insert. Avoid unnecessary value copy. - // The value remains uninitialized. - unsafe fn insert_key(self) -> FullBucketMut<'a, K, V> { - match self.elem { - NeqElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - let uninit = mem::uninitialized(); - robin_hood(bucket, disp, self.hash, self.key, uninit) - }, - NoElem(mut bucket, disp) => { - if disp >= DISPLACEMENT_THRESHOLD { - bucket.table_mut().set_tag(true); - } - bucket.put_key(self.hash, self.key) - }, - } - } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2823,7 +2728,6 @@ mod test_map { use super::RandomState; use cell::RefCell; use rand::{thread_rng, Rng}; - use panic; use realstd::collections::CollectionAllocErr::*; use realstd::mem::size_of; use realstd::usize; @@ -3709,59 +3613,6 @@ mod test_map { panic!("Adaptive early resize failed"); } - #[test] - fn test_placement_in() { - let mut map = HashMap::new(); - map.extend((0..10).map(|i| (i, i))); - - map.entry(100) <- 100; - assert_eq!(map[&100], 100); - - map.entry(0) <- 10; - assert_eq!(map[&0], 10); - - assert_eq!(map.len(), 11); - } - - #[test] - fn test_placement_panic() { - let mut map = HashMap::new(); - map.extend((0..10).map(|i| (i, i))); - - fn mkpanic() -> usize { panic!() } - - // modify existing key - // when panic happens, previous key is removed. - let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(0) <- mkpanic(); })); - assert_eq!(map.len(), 9); - assert!(!map.contains_key(&0)); - - // add new key - let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { map.entry(100) <- mkpanic(); })); - assert_eq!(map.len(), 9); - assert!(!map.contains_key(&100)); - } - - #[test] - fn test_placement_drop() { - // correctly drop - struct TestV<'a>(&'a mut bool); - impl<'a> Drop for TestV<'a> { - fn drop(&mut self) { - if !*self.0 { panic!("value double drop!"); } // no double drop - *self.0 = false; - } - } - - fn makepanic<'a>() -> TestV<'a> { panic!() } - - let mut can_drop = true; - let mut hm = HashMap::new(); - hm.insert(0, TestV(&mut can_drop)); - let _ = panic::catch_unwind(panic::AssertUnwindSafe(|| { hm.entry(0) <- makepanic(); })); - assert_eq!(hm.len(), 0); - } - #[test] fn test_try_reserve() { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index c6861c82a23..fa6053d3f6d 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -486,21 +486,6 @@ impl EmptyBucket table: self.table, } } - - /// Puts given key, remain value uninitialized. - /// It is only used for inplacement insertion. - pub unsafe fn put_key(mut self, hash: SafeHash, key: K) -> FullBucket { - *self.raw.hash() = hash.inspect(); - let pair_ptr = self.raw.pair(); - ptr::write(&mut (*pair_ptr).0, key); - - self.table.borrow_table_mut().size += 1; - - FullBucket { - raw: self.raw, - table: self.table, - } - } } impl>> FullBucket { @@ -576,17 +561,6 @@ impl<'t, K, V> FullBucket> { v) } } - - /// Remove this bucket's `key` from the hashtable. - /// Only used for inplacement insertion. - /// NOTE: `Value` is uninitialized when this function is called, don't try to drop the `Value`. - pub unsafe fn remove_key(&mut self) { - self.table.size -= 1; - - *self.raw.hash() = EMPTY_BUCKET; - let pair_ptr = self.raw.pair(); - ptr::drop_in_place(&mut (*pair_ptr).0); // only drop key - } } // This use of `Put` is misleading and restrictive, but safe and sufficient for our use cases diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 68d3b946d9e..e18e055654b 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -290,7 +290,6 @@ #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(peek)] -#![feature(placement_in_syntax)] #![feature(placement_new_protocol)] #![feature(prelude_import)] #![feature(ptr_internals)] diff --git a/src/libsyntax/ast.rs b/src/libsyntax/ast.rs index c90b0aecfc0..31bb1c88b87 100644 --- a/src/libsyntax/ast.rs +++ b/src/libsyntax/ast.rs @@ -1011,7 +1011,6 @@ impl Expr { pub fn precedence(&self) -> ExprPrecedence { match self.node { ExprKind::Box(_) => ExprPrecedence::Box, - ExprKind::InPlace(..) => ExprPrecedence::InPlace, ExprKind::Array(_) => ExprPrecedence::Array, ExprKind::Call(..) => ExprPrecedence::Call, ExprKind::MethodCall(..) => ExprPrecedence::MethodCall, @@ -1071,8 +1070,6 @@ pub enum RangeLimits { pub enum ExprKind { /// A `box x` expression. Box(P), - /// First expr is the place; second expr is the value. - InPlace(P, P), /// An array (`[a, b, c, d]`) Array(Vec>), /// A function call diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 463e76e1461..e734a4e3735 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -146,7 +146,6 @@ declare_features! ( (active, rustc_diagnostic_macros, "1.0.0", None, None), (active, rustc_const_unstable, "1.0.0", None, None), (active, box_syntax, "1.0.0", Some(27779), None), - (active, placement_in_syntax, "1.0.0", Some(27779), None), (active, unboxed_closures, "1.0.0", Some(29625), None), (active, fundamental, "1.0.0", Some(29635), None), @@ -1287,9 +1286,6 @@ pub const EXPLAIN_VIS_MATCHER: &'static str = pub const EXPLAIN_LIFETIME_MATCHER: &'static str = ":lifetime fragment specifier is experimental and subject to change"; -pub const EXPLAIN_PLACEMENT_IN: &'static str = - "placement-in expression syntax is experimental and subject to change."; - pub const EXPLAIN_UNSIZED_TUPLE_COERCION: &'static str = "Unsized tuple coercion is not stable enough for use and is subject to change"; @@ -1636,9 +1632,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, type_ascription, e.span, "type ascription is experimental"); } - ast::ExprKind::InPlace(..) => { - gate_feature_post!(&self, placement_in_syntax, e.span, EXPLAIN_PLACEMENT_IN); - } ast::ExprKind::Yield(..) => { gate_feature_post!(&self, generators, e.span, diff --git a/src/libsyntax/fold.rs b/src/libsyntax/fold.rs index 05a3150c139..e702bf56e7f 100644 --- a/src/libsyntax/fold.rs +++ b/src/libsyntax/fold.rs @@ -1167,9 +1167,6 @@ pub fn noop_fold_expr(Expr {id, node, span, attrs}: Expr, folder: &mu ExprKind::Box(e) => { ExprKind::Box(folder.fold_expr(e)) } - ExprKind::InPlace(p, e) => { - ExprKind::InPlace(folder.fold_expr(p), folder.fold_expr(e)) - } ExprKind::Array(exprs) => { ExprKind::Array(folder.fold_exprs(exprs)) } diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index f7cdd4ba2b4..f5ab023b30e 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -2850,17 +2850,6 @@ impl<'a> Parser<'a> { let (span, e) = self.interpolated_or_expr_span(e)?; (lo.to(span), ExprKind::AddrOf(m, e)) } - token::Ident(..) if self.token.is_keyword(keywords::In) => { - self.bump(); - let place = self.parse_expr_res( - Restrictions::NO_STRUCT_LITERAL, - None, - )?; - let blk = self.parse_block()?; - let span = blk.span; - let blk_expr = self.mk_expr(span, ExprKind::Block(blk), ThinVec::new()); - (lo.to(span), ExprKind::InPlace(place, blk_expr)) - } token::Ident(..) if self.token.is_keyword(keywords::Box) => { self.bump(); let e = self.parse_prefix_expr(None); @@ -3023,8 +3012,6 @@ impl<'a> Parser<'a> { } AssocOp::Assign => self.mk_expr(span, ExprKind::Assign(lhs, rhs), ThinVec::new()), - AssocOp::Inplace => - self.mk_expr(span, ExprKind::InPlace(lhs, rhs), ThinVec::new()), AssocOp::AssignOp(k) => { let aop = match k { token::Plus => BinOpKind::Add, diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index ae045fc095a..c3785c10f69 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -1877,16 +1877,6 @@ impl<'a> State<'a> { Ok(()) } - fn print_expr_in_place(&mut self, - place: &ast::Expr, - expr: &ast::Expr) -> io::Result<()> { - let prec = AssocOp::Inplace.precedence() as i8; - self.print_expr_maybe_paren(place, prec + 1)?; - self.s.space()?; - self.word_space("<-")?; - self.print_expr_maybe_paren(expr, prec) - } - fn print_expr_vec(&mut self, exprs: &[P], attrs: &[Attribute]) -> io::Result<()> { self.ibox(INDENT_UNIT)?; @@ -2056,9 +2046,6 @@ impl<'a> State<'a> { self.word_space("box")?; self.print_expr_maybe_paren(expr, parser::PREC_PREFIX)?; } - ast::ExprKind::InPlace(ref place, ref expr) => { - self.print_expr_in_place(place, expr)?; - } ast::ExprKind::Array(ref exprs) => { self.print_expr_vec(&exprs[..], attrs)?; } diff --git a/src/libsyntax/util/parser.rs b/src/libsyntax/util/parser.rs index 86963c4000b..4770273e8c4 100644 --- a/src/libsyntax/util/parser.rs +++ b/src/libsyntax/util/parser.rs @@ -56,8 +56,6 @@ pub enum AssocOp { GreaterEqual, /// `=` Assign, - /// `<-` - Inplace, /// `?=` where ? is one of the BinOpToken AssignOp(BinOpToken), /// `as` @@ -86,7 +84,6 @@ impl AssocOp { use self::AssocOp::*; match *t { Token::BinOpEq(k) => Some(AssignOp(k)), - Token::LArrow => Some(Inplace), Token::Eq => Some(Assign), Token::BinOp(BinOpToken::Star) => Some(Multiply), Token::BinOp(BinOpToken::Slash) => Some(Divide), @@ -156,7 +153,6 @@ impl AssocOp { LAnd => 6, LOr => 5, DotDot | DotDotEq => 4, - Inplace => 3, Assign | AssignOp(_) => 2, } } @@ -166,7 +162,7 @@ impl AssocOp { use self::AssocOp::*; // NOTE: it is a bug to have an operators that has same precedence but different fixities! match *self { - Inplace | Assign | AssignOp(_) => Fixity::Right, + Assign | AssignOp(_) => Fixity::Right, As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | LAnd | LOr | Colon => Fixity::Left, @@ -178,7 +174,7 @@ impl AssocOp { use self::AssocOp::*; match *self { Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual => true, - Inplace | Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | + Assign | AssignOp(_) | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => false } @@ -187,7 +183,7 @@ impl AssocOp { pub fn is_assign_like(&self) -> bool { use self::AssocOp::*; match *self { - Assign | AssignOp(_) | Inplace => true, + Assign | AssignOp(_) => true, Less | Greater | LessEqual | GreaterEqual | Equal | NotEqual | As | Multiply | Divide | Modulus | Add | Subtract | ShiftLeft | ShiftRight | BitAnd | BitXor | BitOr | LAnd | LOr | DotDot | DotDotEq | Colon => false @@ -215,7 +211,7 @@ impl AssocOp { BitOr => Some(BinOpKind::BitOr), LAnd => Some(BinOpKind::And), LOr => Some(BinOpKind::Or), - Inplace | Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None + Assign | AssignOp(_) | As | DotDot | DotDotEq | Colon => None } } } @@ -242,7 +238,6 @@ pub enum ExprPrecedence { Binary(BinOpKind), - InPlace, Cast, Type, @@ -310,7 +305,6 @@ impl ExprPrecedence { // Binop-like expr kinds, handled by `AssocOp`. ExprPrecedence::Binary(op) => AssocOp::from_ast_binop(op).precedence() as i8, - ExprPrecedence::InPlace => AssocOp::Inplace.precedence() as i8, ExprPrecedence::Cast => AssocOp::As.precedence() as i8, ExprPrecedence::Type => AssocOp::Colon.precedence() as i8, diff --git a/src/libsyntax/visit.rs b/src/libsyntax/visit.rs index bbf1fe124f1..d8de78054ab 100644 --- a/src/libsyntax/visit.rs +++ b/src/libsyntax/visit.rs @@ -654,10 +654,6 @@ pub fn walk_expr<'a, V: Visitor<'a>>(visitor: &mut V, expression: &'a Expr) { ExprKind::Box(ref subexpression) => { visitor.visit_expr(subexpression) } - ExprKind::InPlace(ref place, ref subexpression) => { - visitor.visit_expr(place); - visitor.visit_expr(subexpression) - } ExprKind::Array(ref subexpressions) => { walk_list!(visitor, visit_expr, subexpressions); } diff --git a/src/libsyntax_pos/hygiene.rs b/src/libsyntax_pos/hygiene.rs index 85a2940ec44..aba71bd0468 100644 --- a/src/libsyntax_pos/hygiene.rs +++ b/src/libsyntax_pos/hygiene.rs @@ -430,7 +430,6 @@ pub enum ExpnFormat { /// The kind of compiler desugaring. #[derive(Clone, Hash, Debug, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub enum CompilerDesugaringKind { - BackArrow, DotFill, QuestionMark, } @@ -439,7 +438,6 @@ impl CompilerDesugaringKind { pub fn as_symbol(&self) -> Symbol { use CompilerDesugaringKind::*; let s = match *self { - BackArrow => "<-", DotFill => "...", QuestionMark => "?", }; diff --git a/src/test/compile-fail/issue-14084.rs b/src/test/compile-fail/issue-14084.rs deleted file mode 100644 index 446514c8dd4..00000000000 --- a/src/test/compile-fail/issue-14084.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(box_syntax)] -#![feature(placement_in_syntax)] - -fn main() { - () <- 0; - //~^ ERROR: `(): std::ops::Placer<_>` is not satisfied -} diff --git a/src/test/compile-fail/placement-expr-unsafe.rs b/src/test/compile-fail/placement-expr-unsafe.rs deleted file mode 100644 index bf6f4c52f1f..00000000000 --- a/src/test/compile-fail/placement-expr-unsafe.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Check that placement in respects unsafe code checks. - -#![feature(box_heap)] -#![feature(placement_in_syntax)] - -fn main() { - use std::boxed::HEAP; - - let p: *const i32 = &42; - let _ = HEAP <- *p; //~ ERROR requires unsafe - - let p: *const _ = &HEAP; - let _ = *p <- 42; //~ ERROR requires unsafe -} diff --git a/src/test/compile-fail/placement-expr-unstable.rs b/src/test/compile-fail/placement-expr-unstable.rs deleted file mode 100644 index 35695efe1a9..00000000000 --- a/src/test/compile-fail/placement-expr-unstable.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Check that placement in respects unstable code checks. - -#![feature(placement_in_syntax)] - -fn main() { - use std::boxed::HEAP; //~ ERROR use of unstable library feature - - let _ = HEAP <- { //~ ERROR use of unstable library feature - HEAP //~ ERROR use of unstable library feature - }; -} diff --git a/src/test/parse-fail/assoc-oddities-1.rs b/src/test/parse-fail/assoc-oddities-1.rs index 5c0c47de58a..63408b76b15 100644 --- a/src/test/parse-fail/assoc-oddities-1.rs +++ b/src/test/parse-fail/assoc-oddities-1.rs @@ -13,10 +13,9 @@ fn that_odd_parse() { // following lines below parse and must not fail x = if c { a } else { b }(); - x <- if c { a } else { b }[n]; x = if true { 1 } else { 0 } as *mut _; // however this does not parse and probably should fail to retain compat? - // NB: `..` here is arbitrary, failure happens/should happen ∀ops that aren’t `=` or `<-` + // NB: `..` here is arbitrary, failure happens/should happen ∀ops that aren’t `=` // see assoc-oddities-2 and assoc-oddities-3 ..if c { a } else { b }[n]; //~ ERROR expected one of } diff --git a/src/test/pretty/stmt_expr_attributes.rs b/src/test/pretty/stmt_expr_attributes.rs index 17e6119f968..b34e1852079 100644 --- a/src/test/pretty/stmt_expr_attributes.rs +++ b/src/test/pretty/stmt_expr_attributes.rs @@ -12,7 +12,6 @@ #![feature(custom_attribute)] #![feature(box_syntax)] -#![feature(placement_in_syntax)] #![feature(stmt_expr_attributes)] fn main() { } diff --git a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs index f3f7777d8d4..62cb870c7bb 100644 --- a/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs +++ b/src/test/run-pass-fulldeps/ast_stmt_expr_attr.rs @@ -126,16 +126,6 @@ fn run() { check_expr_attrs("#[attr] box 0", outer); reject_expr_parse("box #![attr] 0"); - check_expr_attrs("#[attr] 0 <- #[attr] 0", none); - check_expr_attrs("#[attr] (0 <- 0)", outer); - reject_expr_parse("0 #[attr] <- 0"); - reject_expr_parse("0 <- #![attr] 0"); - - check_expr_attrs("in #[attr] 0 {#[attr] 0}", none); - check_expr_attrs("#[attr] (in 0 {0})", outer); - reject_expr_parse("in 0 #[attr] {0}"); - reject_expr_parse("in 0 {#![attr] 0}"); - check_expr_attrs("#[attr] [#![attr]]", both); check_expr_attrs("#[attr] [#![attr] 0]", both); check_expr_attrs("#[attr] [#![attr] 0; 0]", both); diff --git a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs index 05fe274c49f..920760cd34a 100644 --- a/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs +++ b/src/test/run-pass-fulldeps/pprust-expr-roundtrip.rs @@ -84,18 +84,11 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { let mut g = |e| f(expr(e)); - for kind in 0 .. 17 { + for kind in 0 .. 16 { match kind { 0 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Box(e))), - 1 => { - // Note that for binary expressions, we explore each side separately. The - // parenthesization decisions for the LHS and RHS should be independent, and this - // way produces `O(n)` results instead of `O(n^2)`. - iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(e, make_x()))); - iter_exprs(depth - 1, &mut |e| g(ExprKind::InPlace(make_x(), e))); - }, - 2 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), - 3 => { + 1 => iter_exprs(depth - 1, &mut |e| g(ExprKind::Call(e, vec![]))), + 2 => { let seg = PathSegment { identifier: Ident::from_str("x"), span: DUMMY_SP, @@ -107,25 +100,25 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { iter_exprs(depth - 1, &mut |e| g(ExprKind::MethodCall( seg.clone(), vec![make_x(), e]))); }, - 4 => { + 3 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Add }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, - 5 => { + 4 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Mul }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, - 6 => { + 5 => { let op = Spanned { span: DUMMY_SP, node: BinOpKind::Shl }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Binary(op, make_x(), e))); }, - 7 => { + 6 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Unary(UnOp::Deref, e))); }, - 8 => { + 7 => { let block = P(Block { stmts: Vec::new(), id: DUMMY_NODE_ID, @@ -135,7 +128,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { }); iter_exprs(depth - 1, &mut |e| g(ExprKind::If(e, block.clone(), None))); }, - 9 => { + 8 => { let decl = P(FnDecl { inputs: vec![], output: FunctionRetTy::Default(DUMMY_SP), @@ -148,28 +141,28 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { e, DUMMY_SP))); }, - 10 => { + 9 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(e, make_x()))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Assign(make_x(), e))); }, - 11 => { + 10 => { let ident = Spanned { span: DUMMY_SP, node: Ident::from_str("f") }; iter_exprs(depth - 1, &mut |e| g(ExprKind::Field(e, ident))); }, - 12 => { + 11 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( Some(e), Some(make_x()), RangeLimits::HalfOpen))); iter_exprs(depth - 1, &mut |e| g(ExprKind::Range( Some(make_x()), Some(e), RangeLimits::HalfOpen))); }, - 13 => { + 12 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::AddrOf(Mutability::Immutable, e))); }, - 14 => { + 13 => { g(ExprKind::Ret(None)); iter_exprs(depth - 1, &mut |e| g(ExprKind::Ret(Some(e)))); }, - 15 => { + 14 => { let seg = PathSegment { identifier: Ident::from_str("S"), span: DUMMY_SP, @@ -181,7 +174,7 @@ fn iter_exprs(depth: usize, f: &mut FnMut(P)) { }; g(ExprKind::Struct(path, vec![], Some(make_x()))); }, - 16 => { + 15 => { iter_exprs(depth - 1, &mut |e| g(ExprKind::Try(e))); }, _ => panic!("bad counter value in iter_exprs"), diff --git a/src/test/run-pass/new-box-syntax.rs b/src/test/run-pass/new-box-syntax.rs index 34687c6ca1d..6598b70b3d5 100644 --- a/src/test/run-pass/new-box-syntax.rs +++ b/src/test/run-pass/new-box-syntax.rs @@ -14,16 +14,11 @@ * http://creativecommons.org/publicdomain/zero/1.0/ */ #![allow(dead_code, unused_variables)] -#![feature(box_syntax, box_heap)] -#![feature(placement_in_syntax)] - -// during check-pretty, the expanded code needs to opt into these -// features -#![feature(placement_new_protocol, core_intrinsics)] +#![feature(box_syntax)] // Tests that the new `box` syntax works with unique pointers. -use std::boxed::{Box, HEAP}; +use std::boxed::Box; struct Structure { x: isize, @@ -31,7 +26,6 @@ struct Structure { } pub fn main() { - let x: Box = in HEAP { 2 }; let y: Box = box 2; let b: Box = box (1 + 2); let c = box (3 + 4); diff --git a/src/test/run-pass/placement-in-syntax.rs b/src/test/run-pass/placement-in-syntax.rs deleted file mode 100644 index 7bda9ae2524..00000000000 --- a/src/test/run-pass/placement-in-syntax.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(dead_code, unused_variables)] -#![feature(box_heap)] -#![feature(placement_in_syntax)] - -// Tests that the new `in` syntax works with unique pointers. -// -// Compare with new-box-syntax.rs - -use std::boxed::{Box, HEAP}; - -struct Structure { - x: isize, - y: isize, -} - -pub fn main() { - let x: Box = in HEAP { 2 }; - let b: Box = in HEAP { 1 + 2 }; - let c = in HEAP { 3 + 4 }; - - let s: Box = in HEAP { - Structure { - x: 3, - y: 4, - } - }; -} diff --git a/src/test/ui/feature-gate-placement-expr.rs b/src/test/ui/feature-gate-placement-expr.rs deleted file mode 100644 index e3478876763..00000000000 --- a/src/test/ui/feature-gate-placement-expr.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// gate-test-placement_in_syntax - -// Check that `in PLACE { EXPR }` is feature-gated. -// -// See also feature-gate-box-expr.rs -// -// (Note that the two tests are separated since the checks appear to -// be performed at distinct phases, with an abort_if_errors call -// separating them.) - -fn main() { - use std::boxed::HEAP; - - let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental - println!("x: {}", x); -} diff --git a/src/test/ui/feature-gate-placement-expr.stderr b/src/test/ui/feature-gate-placement-expr.stderr deleted file mode 100644 index b5c091763a1..00000000000 --- a/src/test/ui/feature-gate-placement-expr.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0658]: placement-in expression syntax is experimental and subject to change. (see issue #27779) - --> $DIR/feature-gate-placement-expr.rs:24:13 - | -LL | let x = HEAP <- 'c'; //~ ERROR placement-in expression syntax is experimental - | ^^^^^^^^^^^ - | - = help: add #![feature(placement_in_syntax)] to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From da0ceeff5a7839427c751cf056e16e67217b12ea Mon Sep 17 00:00:00 2001 From: Dirkjan Ochtman Date: Sun, 1 Apr 2018 16:55:25 +0200 Subject: Introduce Vec::resize_with method (see #41758) --- src/liballoc/vec.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 57 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2eedb964f88..0fd4178bd95 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1306,6 +1306,49 @@ impl Vec { } other } + + /// Resizes the `Vec` in-place so that `len` is equal to `new_len`. + /// + /// If `new_len` is greater than `len`, the `Vec` is extended by the + /// difference, with each additional slot filled with the result of + /// calling the closure `f`. The return values from `f` will end up + /// in the `Vec` in the order they have been generated. + /// + /// If `new_len` is less than `len`, the `Vec` is simply truncated. + /// + /// This method uses a closure to create new values on every push. If + /// you'd rather [`Clone`] a given value, use [`resize`]. If you want + /// to use the [`Default`] trait to generate values, you can pass + /// [`Default::default()`] as the second argument.. + /// + /// # Examples + /// + /// ``` + /// #![feature(vec_resize_with)] + /// + /// let mut vec = vec![1, 2, 3]; + /// vec.resize_with(5, Default::default); + /// assert_eq!(vec, [1, 2, 3, 0, 0]); + /// + /// let mut vec = vec![]; + /// let mut p = 1; + /// vec.resize_with(4, || { p *= 2; p }); + /// assert_eq!(vec, [2, 4, 8, 16]); + /// ``` + /// + /// [`resize`]: #method.resize + /// [`Clone`]: ../../std/clone/trait.Clone.html + #[unstable(feature = "vec_resize_with", issue = "41758")] + pub fn resize_with(&mut self, new_len: usize, f: F) + where F: FnMut() -> T + { + let len = self.len(); + if new_len > len { + self.extend_with(new_len - len, ExtendFunc(f)); + } else { + self.truncate(new_len); + } + } } impl Vec { @@ -1316,8 +1359,8 @@ impl Vec { /// If `new_len` is less than `len`, the `Vec` is simply truncated. /// /// This method requires [`Clone`] to be able clone the passed value. If - /// you'd rather create a value with [`Default`] instead, see - /// [`resize_default`]. + /// you need more flexibility (or want to rely on [`Default`] instead of + /// [`Clone`]), use [`resize_with`]. /// /// # Examples /// @@ -1333,7 +1376,7 @@ impl Vec { /// /// [`Clone`]: ../../std/clone/trait.Clone.html /// [`Default`]: ../../std/default/trait.Default.html - /// [`resize_default`]: #method.resize_default + /// [`resize_with`]: #method.resize_with #[stable(feature = "vec_resize", since = "1.5.0")] pub fn resize(&mut self, new_len: usize, value: T) { let len = self.len(); @@ -1412,24 +1455,31 @@ impl Vec { // This code generalises `extend_with_{element,default}`. trait ExtendWith { - fn next(&self) -> T; + fn next(&mut self) -> T; fn last(self) -> T; } struct ExtendElement(T); impl ExtendWith for ExtendElement { - fn next(&self) -> T { self.0.clone() } + fn next(&mut self) -> T { self.0.clone() } fn last(self) -> T { self.0 } } struct ExtendDefault; impl ExtendWith for ExtendDefault { - fn next(&self) -> T { Default::default() } + fn next(&mut self) -> T { Default::default() } fn last(self) -> T { Default::default() } } + +struct ExtendFunc(F); +impl T> ExtendWith for ExtendFunc { + fn next(&mut self) -> T { (self.0)() } + fn last(mut self) -> T { (self.0)() } +} + impl Vec { /// Extend the vector by `n` values, using the given generator. - fn extend_with>(&mut self, n: usize, value: E) { + fn extend_with>(&mut self, n: usize, mut value: E) { self.reserve(n); unsafe { -- cgit 1.4.1-3-g733a5 From 5c58eec0bd8cee8fb2a191396d5ad5b5c9b0116a Mon Sep 17 00:00:00 2001 From: Clar Charr Date: Wed, 4 Apr 2018 19:10:38 -0400 Subject: Replace manual iter exhaust with for_each(drop). --- src/liballoc/btree/map.rs | 3 +-- src/liballoc/linked_list.rs | 2 +- src/liballoc/vec.rs | 9 +++------ src/liballoc/vec_deque.rs | 2 +- src/librustc_data_structures/array_vec.rs | 4 ++-- src/libstd/collections/hash/table.rs | 2 +- 6 files changed, 9 insertions(+), 13 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index ed9c8c18f0d..37274a3c3ec 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -1260,8 +1260,7 @@ impl IntoIterator for BTreeMap { #[stable(feature = "btree_drop", since = "1.7.0")] impl Drop for IntoIter { fn drop(&mut self) { - for _ in &mut *self { - } + self.for_each(drop); unsafe { let leaf_node = ptr::read(&self.front).into_node(); if let Some(first_parent) = leaf_node.deallocate_and_ascend() { diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs index 097d2e414f5..b633787fadf 100644 --- a/src/liballoc/linked_list.rs +++ b/src/liballoc/linked_list.rs @@ -1076,7 +1076,7 @@ impl<'a, T, F> Drop for DrainFilter<'a, T, F> where F: FnMut(&mut T) -> bool, { fn drop(&mut self) { - for _ in self { } + self.for_each(drop); } } diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 2f57c53a6d8..635ffe08e9c 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2354,7 +2354,7 @@ impl<'a, T> DoubleEndedIterator for Drain<'a, T> { impl<'a, T> Drop for Drain<'a, T> { fn drop(&mut self) { // exhaust self first - while let Some(_) = self.next() {} + self.for_each(drop); if self.tail_len > 0 { unsafe { @@ -2474,9 +2474,7 @@ impl<'a, I: Iterator> ExactSizeIterator for Splice<'a, I> {} #[stable(feature = "vec_splice", since = "1.21.0")] impl<'a, I: Iterator> Drop for Splice<'a, I> { fn drop(&mut self) { - // exhaust drain first - while let Some(_) = self.drain.next() {} - + self.drain.by_ref().for_each(drop); unsafe { if self.drain.tail_len == 0 { @@ -2605,8 +2603,7 @@ impl<'a, T, F> Drop for DrainFilter<'a, T, F> where F: FnMut(&mut T) -> bool, { fn drop(&mut self) { - for _ in self.by_ref() { } - + self.for_each(drop); unsafe { self.vec.set_len(self.old_len - self.del); } diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index 68add3cbd51..ee9d8e796ab 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -2177,7 +2177,7 @@ unsafe impl<'a, T: Send> Send for Drain<'a, T> {} #[stable(feature = "drain", since = "1.6.0")] impl<'a, T: 'a> Drop for Drain<'a, T> { fn drop(&mut self) { - for _ in self.by_ref() {} + self.for_each(drop); let source_deque = unsafe { self.deque.as_mut() }; diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 511c407d45a..34e19bba08b 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -207,7 +207,7 @@ pub struct Iter { impl Drop for Iter { fn drop(&mut self) { - for _ in self {} + self.for_each(drop); } } @@ -251,7 +251,7 @@ impl<'a, A: Array> Iterator for Drain<'a, A> { impl<'a, A: Array> Drop for Drain<'a, A> { fn drop(&mut self) { // exhaust self first - while let Some(_) = self.next() {} + self.for_each(drop); if self.tail_len > 0 { unsafe { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 73bd5747c10..4ed1e159a0a 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1129,7 +1129,7 @@ impl<'a, K, V> ExactSizeIterator for Drain<'a, K, V> { impl<'a, K: 'a, V: 'a> Drop for Drain<'a, K, V> { fn drop(&mut self) { - for _ in self {} + self.for_each(drop); } } -- cgit 1.4.1-3-g733a5 From 1a2a23447e451faee7ffbffab2be8831f3098f6d Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Thu, 8 Mar 2018 22:24:10 +0300 Subject: Stabilize attributes on generic parameters --- src/liballoc/lib.rs | 2 +- src/libarena/lib.rs | 2 +- src/librustc_typeck/diagnostics.rs | 1 - src/libstd/lib.rs | 2 +- src/libsyntax/feature_gate.rs | 20 +-- .../attrs-with-no-formal-in-generics-1.rs | 3 +- .../attrs-with-no-formal-in-generics-2.rs | 3 +- src/test/compile-fail/synthetic-param.rs | 2 +- .../mir-opt/end_region_destruction_extents_1.rs | 1 - src/test/run-pass/attr-on-generic-formals.rs | 2 +- .../auxiliary/dropck_eyepatch_extern_crate.rs | 1 - src/test/run-pass/dropck-eyepatch-reorder.rs | 1 - src/test/run-pass/dropck-eyepatch.rs | 1 - .../auxiliary/dropck_eyepatch_extern_crate.rs | 1 - .../dropck/dropck-eyepatch-implies-unsafe-impl.rs | 1 - .../dropck-eyepatch-implies-unsafe-impl.stderr | 4 +- src/test/ui/dropck/dropck-eyepatch-reorder.rs | 1 - src/test/ui/dropck/dropck-eyepatch-reorder.stderr | 8 +- src/test/ui/dropck/dropck-eyepatch.rs | 1 - src/test/ui/dropck/dropck-eyepatch.stderr | 8 +- src/test/ui/feature-gate-custom_attribute2.rs | 7 -- src/test/ui/feature-gate-custom_attribute2.stderr | 34 ++--- src/test/ui/feature-gate-generic_param_attrs.rs | 76 ----------- .../ui/feature-gate-generic_param_attrs.stderr | 139 --------------------- src/test/ui/feature-gate-may-dangle.rs | 2 - src/test/ui/feature-gate-may-dangle.stderr | 2 +- src/test/ui/generic-param-attrs.rs | 54 ++++++++ src/test/ui/nll/drop-may-dangle.rs | 1 - src/test/ui/nll/drop-no-may-dangle.rs | 1 - src/test/ui/nll/drop-no-may-dangle.stderr | 4 +- 30 files changed, 93 insertions(+), 292 deletions(-) delete mode 100644 src/test/ui/feature-gate-generic_param_attrs.rs delete mode 100644 src/test/ui/feature-gate-generic_param_attrs.stderr create mode 100644 src/test/ui/generic-param-attrs.rs (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 2fad3b0bad4..cbbea6c19c8 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -97,7 +97,7 @@ #![feature(fmt_internals)] #![feature(from_ref)] #![feature(fundamental)] -#![feature(generic_param_attrs)] +#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(stage0, feature(i128_type))] #![feature(iter_rfold)] #![feature(lang_items)] diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 72fa3148fe5..7eaf67e6ea6 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -27,7 +27,7 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![feature(generic_param_attrs)] +#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(test, feature(test))] #![allow(deprecated)] diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index 99726bb65f3..79d7c8e7282 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -3784,7 +3784,6 @@ that impl must be declared as an `unsafe impl. Erroneous code example: ```compile_fail,E0569 -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] struct Foo(X); diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index e18e055654b..3f1fec4c317 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -266,7 +266,7 @@ #![feature(float_from_str_radix)] #![feature(fn_traits)] #![feature(fnbox)] -#![feature(generic_param_attrs)] +#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(hashmap_internals)] #![feature(heap_api)] #![cfg_attr(stage0, feature(i128_type, i128))] diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index e734a4e3735..1cf62a8bf33 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -288,9 +288,6 @@ declare_features! ( // rustc internal (active, compiler_builtins, "1.13.0", None, None), - // Allows attributes on lifetime/type formal parameters in generics (RFC 1327) - (active, generic_param_attrs, "1.11.0", Some(34761), None), - // Allows #[link(..., cfg(..))] (active, link_cfg, "1.14.0", Some(37406), None), @@ -566,6 +563,8 @@ declare_features! ( (accepted, match_default_bindings, "1.26.0", Some(42640), None), // allow `'_` placeholder lifetimes (accepted, underscore_lifetimes, "1.26.0", Some(44524), None), + // Allows attributes on lifetime/type formal parameters in generics (RFC 1327) + (accepted, generic_param_attrs, "1.26.0", Some(48848), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1775,21 +1774,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { } visit::walk_vis(self, vis); } - - fn visit_generic_param(&mut self, param: &'a ast::GenericParam) { - let (attrs, explain) = match *param { - ast::GenericParam::Lifetime(ref ld) => - (&ld.attrs, "attributes on lifetime bindings are experimental"), - ast::GenericParam::Type(ref t) => - (&t.attrs, "attributes on type parameter bindings are experimental"), - }; - - if !attrs.is_empty() { - gate_feature_post!(&self, generic_param_attrs, attrs[0].span, explain); - } - - visit::walk_generic_param(self, param) - } } pub fn get_features(span_handler: &Handler, krate_attrs: &[ast::Attribute], diff --git a/src/test/compile-fail/attrs-with-no-formal-in-generics-1.rs b/src/test/compile-fail/attrs-with-no-formal-in-generics-1.rs index 53e287cda20..ec7885f1f44 100644 --- a/src/test/compile-fail/attrs-with-no-formal-in-generics-1.rs +++ b/src/test/compile-fail/attrs-with-no-formal-in-generics-1.rs @@ -12,8 +12,7 @@ // `#[oops]` is left dangling (that is, it is unattached, with no // formal binding following it). -#![feature(generic_param_attrs, rustc_attrs)] -#![allow(dead_code)] +#![feature(rustc_attrs)] struct RefIntPair<'a, 'b>(&'a u32, &'b u32); diff --git a/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs b/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs index 5e09473ab77..efe2d5561a8 100644 --- a/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs +++ b/src/test/compile-fail/attrs-with-no-formal-in-generics-2.rs @@ -12,8 +12,7 @@ // `#[oops]` is left dangling (that is, it is unattached, with no // formal binding following it). -#![feature(generic_param_attrs, rustc_attrs)] -#![allow(dead_code)] +#![feature(rustc_attrs)] struct RefAny<'a, T>(&'a T); diff --git a/src/test/compile-fail/synthetic-param.rs b/src/test/compile-fail/synthetic-param.rs index a9762e383fe..337cae1369e 100644 --- a/src/test/compile-fail/synthetic-param.rs +++ b/src/test/compile-fail/synthetic-param.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs, rustc_attrs)] +#![feature(rustc_attrs)] fn func<#[rustc_synthetic] T>(_: T) {} diff --git a/src/test/mir-opt/end_region_destruction_extents_1.rs b/src/test/mir-opt/end_region_destruction_extents_1.rs index 69c5cdccf49..e189f2e3b34 100644 --- a/src/test/mir-opt/end_region_destruction_extents_1.rs +++ b/src/test/mir-opt/end_region_destruction_extents_1.rs @@ -14,7 +14,6 @@ // A scenario with significant destruction code extents (which have // suffix "dce" in current `-Z identify_regions` rendering). -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] fn main() { diff --git a/src/test/run-pass/attr-on-generic-formals.rs b/src/test/run-pass/attr-on-generic-formals.rs index 5985284d849..e87b9e3d82a 100644 --- a/src/test/run-pass/attr-on-generic-formals.rs +++ b/src/test/run-pass/attr-on-generic-formals.rs @@ -17,7 +17,7 @@ // using `rustc_attrs` feature. There is a separate compile-fail/ test // ensuring that the attribute feature-gating works in this context.) -#![feature(generic_param_attrs, rustc_attrs)] +#![feature(rustc_attrs)] #![allow(dead_code)] struct StLt<#[rustc_lt_struct] 'a>(&'a u32); diff --git a/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs b/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs index 1266e589b12..d8912943441 100644 --- a/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs +++ b/src/test/run-pass/auxiliary/dropck_eyepatch_extern_crate.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // The point of this test is to illustrate that the `#[may_dangle]` diff --git a/src/test/run-pass/dropck-eyepatch-reorder.rs b/src/test/run-pass/dropck-eyepatch-reorder.rs index bbf8bb8c352..a99a7232e9e 100644 --- a/src/test/run-pass/dropck-eyepatch-reorder.rs +++ b/src/test/run-pass/dropck-eyepatch-reorder.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // The point of this test is to test uses of `#[may_dangle]` attribute diff --git a/src/test/run-pass/dropck-eyepatch.rs b/src/test/run-pass/dropck-eyepatch.rs index 4a09ba05dff..c0c091d78eb 100644 --- a/src/test/run-pass/dropck-eyepatch.rs +++ b/src/test/run-pass/dropck-eyepatch.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // The point of this test is to illustrate that the `#[may_dangle]` diff --git a/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs b/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs index 1b00d88dcb3..08722ca62ac 100644 --- a/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs +++ b/src/test/ui/dropck/auxiliary/dropck_eyepatch_extern_crate.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // This is a support file for ../dropck-eyepatch-extern-crate.rs diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs index f92c8703dc9..cba438b02a9 100644 --- a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs +++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // This test ensures that a use of `#[may_dangle]` is rejected if diff --git a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr index f4ea7f1bc50..9d68ff13ef3 100644 --- a/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-implies-unsafe-impl.stderr @@ -1,5 +1,5 @@ error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute - --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:32:1 + --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:31:1 | LL | / impl<#[may_dangle] A, B: fmt::Debug> Drop for Pt { LL | | //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute @@ -10,7 +10,7 @@ LL | | } | |_^ error[E0569]: requires an `unsafe impl` declaration due to `#[may_dangle]` attribute - --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:38:1 + --> $DIR/dropck-eyepatch-implies-unsafe-impl.rs:37:1 | LL | / impl<#[may_dangle] 'a, 'b, B: fmt::Debug> Drop for Pr<'a, 'b, B> { LL | | //~^ ERROR requires an `unsafe impl` declaration due to `#[may_dangle]` attribute diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.rs b/src/test/ui/dropck/dropck-eyepatch-reorder.rs index 3bd9efb32b3..eda8d85f6ec 100644 --- a/src/test/ui/dropck/dropck-eyepatch-reorder.rs +++ b/src/test/ui/dropck/dropck-eyepatch-reorder.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // The point of this test is to test uses of `#[may_dangle]` attribute diff --git a/src/test/ui/dropck/dropck-eyepatch-reorder.stderr b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr index e6ce53402f4..1a35996a0ca 100644 --- a/src/test/ui/dropck/dropck-eyepatch-reorder.stderr +++ b/src/test/ui/dropck/dropck-eyepatch-reorder.stderr @@ -1,5 +1,5 @@ error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch-reorder.rs:57:20 + --> $DIR/dropck-eyepatch-reorder.rs:56:20 | LL | dt = Dt("dt", &c); | ^ borrowed value does not live long enough @@ -10,7 +10,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch-reorder.rs:59:20 + --> $DIR/dropck-eyepatch-reorder.rs:58:20 | LL | dr = Dr("dr", &c); | ^ borrowed value does not live long enough @@ -21,7 +21,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch-reorder.rs:67:29 + --> $DIR/dropck-eyepatch-reorder.rs:66:29 | LL | pt = Pt("pt", &c_long, &c); | ^ borrowed value does not live long enough @@ -32,7 +32,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch-reorder.rs:69:29 + --> $DIR/dropck-eyepatch-reorder.rs:68:29 | LL | pr = Pr("pr", &c_long, &c); | ^ borrowed value does not live long enough diff --git a/src/test/ui/dropck/dropck-eyepatch.rs b/src/test/ui/dropck/dropck-eyepatch.rs index abaae47189f..af173a2e979 100644 --- a/src/test/ui/dropck/dropck-eyepatch.rs +++ b/src/test/ui/dropck/dropck-eyepatch.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(generic_param_attrs)] #![feature(dropck_eyepatch)] // The point of this test is to illustrate that the `#[may_dangle]` diff --git a/src/test/ui/dropck/dropck-eyepatch.stderr b/src/test/ui/dropck/dropck-eyepatch.stderr index 5e0a4a74421..4d291642022 100644 --- a/src/test/ui/dropck/dropck-eyepatch.stderr +++ b/src/test/ui/dropck/dropck-eyepatch.stderr @@ -1,5 +1,5 @@ error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch.rs:80:20 + --> $DIR/dropck-eyepatch.rs:79:20 | LL | dt = Dt("dt", &c); | ^ borrowed value does not live long enough @@ -10,7 +10,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch.rs:82:20 + --> $DIR/dropck-eyepatch.rs:81:20 | LL | dr = Dr("dr", &c); | ^ borrowed value does not live long enough @@ -21,7 +21,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch.rs:90:29 + --> $DIR/dropck-eyepatch.rs:89:29 | LL | pt = Pt("pt", &c_long, &c); | ^ borrowed value does not live long enough @@ -32,7 +32,7 @@ LL | } = note: values in a scope are dropped in the opposite order they are created error[E0597]: `c` does not live long enough - --> $DIR/dropck-eyepatch.rs:92:29 + --> $DIR/dropck-eyepatch.rs:91:29 | LL | pr = Pr("pr", &c_long, &c); | ^ borrowed value does not live long enough diff --git a/src/test/ui/feature-gate-custom_attribute2.rs b/src/test/ui/feature-gate-custom_attribute2.rs index 0d89c52d885..30fd89f091b 100644 --- a/src/test/ui/feature-gate-custom_attribute2.rs +++ b/src/test/ui/feature-gate-custom_attribute2.rs @@ -10,16 +10,9 @@ // This test ensures that attributes on formals in generic parameter // lists are included when we are checking for unstable attributes. -// -// Note that feature(generic_param_attrs) *is* enabled here. We are -// checking feature-gating of the attributes themselves, not the -// capability to parse such attributes in that context. // gate-test-custom_attribute -#![feature(generic_param_attrs)] -#![allow(dead_code)] - struct StLt<#[lt_struct] 'a>(&'a u32); //~^ ERROR The attribute `lt_struct` is currently unknown to the compiler struct StTy<#[ty_struct] I>(I); diff --git a/src/test/ui/feature-gate-custom_attribute2.stderr b/src/test/ui/feature-gate-custom_attribute2.stderr index 90be45a33ea..1c1f50366d6 100644 --- a/src/test/ui/feature-gate-custom_attribute2.stderr +++ b/src/test/ui/feature-gate-custom_attribute2.stderr @@ -1,5 +1,5 @@ error[E0658]: The attribute `lt_struct` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:23:13 + --> $DIR/feature-gate-custom_attribute2.rs:16:13 | LL | struct StLt<#[lt_struct] 'a>(&'a u32); | ^^^^^^^^^^^^ @@ -7,7 +7,7 @@ LL | struct StLt<#[lt_struct] 'a>(&'a u32); = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_struct` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:25:13 + --> $DIR/feature-gate-custom_attribute2.rs:18:13 | LL | struct StTy<#[ty_struct] I>(I); | ^^^^^^^^^^^^ @@ -15,7 +15,7 @@ LL | struct StTy<#[ty_struct] I>(I); = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_enum` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:28:11 + --> $DIR/feature-gate-custom_attribute2.rs:21:11 | LL | enum EnLt<#[lt_enum] 'b> { A(&'b u32), B } | ^^^^^^^^^^ @@ -23,7 +23,7 @@ LL | enum EnLt<#[lt_enum] 'b> { A(&'b u32), B } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_enum` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:30:11 + --> $DIR/feature-gate-custom_attribute2.rs:23:11 | LL | enum EnTy<#[ty_enum] J> { A(J), B } | ^^^^^^^^^^ @@ -31,7 +31,7 @@ LL | enum EnTy<#[ty_enum] J> { A(J), B } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_trait` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:33:12 + --> $DIR/feature-gate-custom_attribute2.rs:26:12 | LL | trait TrLt<#[lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; } | ^^^^^^^^^^^ @@ -39,7 +39,7 @@ LL | trait TrLt<#[lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_trait` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:35:12 + --> $DIR/feature-gate-custom_attribute2.rs:28:12 | LL | trait TrTy<#[ty_trait] K> { fn foo(&self, _: K); } | ^^^^^^^^^^^ @@ -47,7 +47,7 @@ LL | trait TrTy<#[ty_trait] K> { fn foo(&self, _: K); } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_type` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:38:11 + --> $DIR/feature-gate-custom_attribute2.rs:31:11 | LL | type TyLt<#[lt_type] 'd> = &'d u32; | ^^^^^^^^^^ @@ -55,7 +55,7 @@ LL | type TyLt<#[lt_type] 'd> = &'d u32; = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_type` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:40:11 + --> $DIR/feature-gate-custom_attribute2.rs:33:11 | LL | type TyTy<#[ty_type] L> = (L, ); | ^^^^^^^^^^ @@ -63,7 +63,7 @@ LL | type TyTy<#[ty_type] L> = (L, ); = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_inherent` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:43:6 + --> $DIR/feature-gate-custom_attribute2.rs:36:6 | LL | impl<#[lt_inherent] 'e> StLt<'e> { } | ^^^^^^^^^^^^^^ @@ -71,7 +71,7 @@ LL | impl<#[lt_inherent] 'e> StLt<'e> { } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_inherent` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:45:6 + --> $DIR/feature-gate-custom_attribute2.rs:38:6 | LL | impl<#[ty_inherent] M> StTy { } | ^^^^^^^^^^^^^^ @@ -79,7 +79,7 @@ LL | impl<#[ty_inherent] M> StTy { } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_impl_for` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:48:6 + --> $DIR/feature-gate-custom_attribute2.rs:41:6 | LL | impl<#[lt_impl_for] 'f> TrLt<'f> for StLt<'f> { | ^^^^^^^^^^^^^^ @@ -87,7 +87,7 @@ LL | impl<#[lt_impl_for] 'f> TrLt<'f> for StLt<'f> { = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_impl_for` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:52:6 + --> $DIR/feature-gate-custom_attribute2.rs:45:6 | LL | impl<#[ty_impl_for] N> TrTy for StTy { | ^^^^^^^^^^^^^^ @@ -95,7 +95,7 @@ LL | impl<#[ty_impl_for] N> TrTy for StTy { = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_fn` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:57:9 + --> $DIR/feature-gate-custom_attribute2.rs:50:9 | LL | fn f_lt<#[lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } } | ^^^^^^^^ @@ -103,7 +103,7 @@ LL | fn f_lt<#[lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_fn` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:59:9 + --> $DIR/feature-gate-custom_attribute2.rs:52:9 | LL | fn f_ty<#[ty_fn] O>(_: O) { } | ^^^^^^^^ @@ -111,7 +111,7 @@ LL | fn f_ty<#[ty_fn] O>(_: O) { } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_meth` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:63:13 + --> $DIR/feature-gate-custom_attribute2.rs:56:13 | LL | fn m_lt<#[lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } } | ^^^^^^^^^^ @@ -119,7 +119,7 @@ LL | fn m_lt<#[lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `ty_meth` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:65:13 + --> $DIR/feature-gate-custom_attribute2.rs:58:13 | LL | fn m_ty<#[ty_meth] P>(_: P) { } | ^^^^^^^^^^ @@ -127,7 +127,7 @@ LL | fn m_ty<#[ty_meth] P>(_: P) { } = help: add #![feature(custom_attribute)] to the crate attributes to enable error[E0658]: The attribute `lt_hof` is currently unknown to the compiler and may have meaning added to it in the future (see issue #29642) - --> $DIR/feature-gate-custom_attribute2.rs:70:19 + --> $DIR/feature-gate-custom_attribute2.rs:63:19 | LL | where Q: for <#[lt_hof] 'i> Fn(&'i [u32]) -> &'i u32 | ^^^^^^^^^ diff --git a/src/test/ui/feature-gate-generic_param_attrs.rs b/src/test/ui/feature-gate-generic_param_attrs.rs deleted file mode 100644 index 944802f450a..00000000000 --- a/src/test/ui/feature-gate-generic_param_attrs.rs +++ /dev/null @@ -1,76 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This test ensures that attributes on formals in generic parameter -// lists are rejected if feature(generic_param_attrs) is not enabled. -// -// (We are prefixing all tested features with `rustc_`, to ensure that -// the attributes themselves won't be rejected by the compiler when -// using `rustc_attrs` feature. There is a separate compile-fail/ test -// ensuring that the attribute feature-gating works in this context.) - -#![feature(rustc_attrs)] -#![allow(dead_code)] - -struct StLt<#[rustc_lt_struct] 'a>(&'a u32); -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -struct StTy<#[rustc_ty_struct] I>(I); -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -enum EnLt<#[rustc_lt_enum] 'b> { A(&'b u32), B } -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -enum EnTy<#[rustc_ty_enum] J> { A(J), B } -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -trait TrLt<#[rustc_lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; } -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -trait TrTy<#[rustc_ty_trait] K> { fn foo(&self, _: K); } -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -type TyLt<#[rustc_lt_type] 'd> = &'d u32; -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -type TyTy<#[rustc_ty_type] L> = (L, ); -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -impl<#[rustc_lt_inherent] 'e> StLt<'e> { } -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -impl<#[rustc_ty_inherent] M> StTy { } -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -impl<#[rustc_lt_impl_for] 'f> TrLt<'f> for StLt<'f> { - //~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) - fn foo(&self, _: &'f [u32]) -> &'f u32 { loop { } } -} -impl<#[rustc_ty_impl_for] N> TrTy for StTy { - //~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - fn foo(&self, _: N) { } -} - -fn f_lt<#[rustc_lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } } -//~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -fn f_ty<#[rustc_ty_fn] O>(_: O) { } -//~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) - -impl StTy { - fn m_lt<#[rustc_lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } } - //~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) - fn m_ty<#[rustc_ty_meth] P>(_: P) { } - //~^ ERROR attributes on type parameter bindings are experimental (see issue #34761) -} - -fn hof_lt(_: Q) - where Q: for <#[rustc_lt_hof] 'i> Fn(&'i [u32]) -> &'i u32 - //~^ ERROR attributes on lifetime bindings are experimental (see issue #34761) -{ -} - -fn main() { - -} diff --git a/src/test/ui/feature-gate-generic_param_attrs.stderr b/src/test/ui/feature-gate-generic_param_attrs.stderr deleted file mode 100644 index 7b449242c32..00000000000 --- a/src/test/ui/feature-gate-generic_param_attrs.stderr +++ /dev/null @@ -1,139 +0,0 @@ -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:22:13 - | -LL | struct StLt<#[rustc_lt_struct] 'a>(&'a u32); - | ^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:24:13 - | -LL | struct StTy<#[rustc_ty_struct] I>(I); - | ^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:27:11 - | -LL | enum EnLt<#[rustc_lt_enum] 'b> { A(&'b u32), B } - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:29:11 - | -LL | enum EnTy<#[rustc_ty_enum] J> { A(J), B } - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:32:12 - | -LL | trait TrLt<#[rustc_lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; } - | ^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:34:12 - | -LL | trait TrTy<#[rustc_ty_trait] K> { fn foo(&self, _: K); } - | ^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:37:11 - | -LL | type TyLt<#[rustc_lt_type] 'd> = &'d u32; - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:39:11 - | -LL | type TyTy<#[rustc_ty_type] L> = (L, ); - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:42:6 - | -LL | impl<#[rustc_lt_inherent] 'e> StLt<'e> { } - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:44:6 - | -LL | impl<#[rustc_ty_inherent] M> StTy { } - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:47:6 - | -LL | impl<#[rustc_lt_impl_for] 'f> TrLt<'f> for StLt<'f> { - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:51:6 - | -LL | impl<#[rustc_ty_impl_for] N> TrTy for StTy { - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:56:9 - | -LL | fn f_lt<#[rustc_lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } } - | ^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:58:9 - | -LL | fn f_ty<#[rustc_ty_fn] O>(_: O) { } - | ^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:62:13 - | -LL | fn m_lt<#[rustc_lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } } - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on type parameter bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:64:13 - | -LL | fn m_ty<#[rustc_ty_meth] P>(_: P) { } - | ^^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error[E0658]: attributes on lifetime bindings are experimental (see issue #34761) - --> $DIR/feature-gate-generic_param_attrs.rs:69:19 - | -LL | where Q: for <#[rustc_lt_hof] 'i> Fn(&'i [u32]) -> &'i u32 - | ^^^^^^^^^^^^^^^ - | - = help: add #![feature(generic_param_attrs)] to the crate attributes to enable - -error: aborting due to 17 previous errors - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/feature-gate-may-dangle.rs b/src/test/ui/feature-gate-may-dangle.rs index ace9fe9ab27..a67ece04488 100644 --- a/src/test/ui/feature-gate-may-dangle.rs +++ b/src/test/ui/feature-gate-may-dangle.rs @@ -12,8 +12,6 @@ // Check that `may_dangle` is rejected if `dropck_eyepatch` feature gate is absent. -#![feature(generic_param_attrs)] - struct Pt(A); impl<#[may_dangle] A> Drop for Pt { //~^ ERROR may_dangle has unstable semantics and may be removed in the future diff --git a/src/test/ui/feature-gate-may-dangle.stderr b/src/test/ui/feature-gate-may-dangle.stderr index 85707f6e921..aad725dfe65 100644 --- a/src/test/ui/feature-gate-may-dangle.stderr +++ b/src/test/ui/feature-gate-may-dangle.stderr @@ -1,5 +1,5 @@ error[E0658]: may_dangle has unstable semantics and may be removed in the future (see issue #34761) - --> $DIR/feature-gate-may-dangle.rs:18:6 + --> $DIR/feature-gate-may-dangle.rs:16:6 | LL | impl<#[may_dangle] A> Drop for Pt { | ^^^^^^^^^^^^^ diff --git a/src/test/ui/generic-param-attrs.rs b/src/test/ui/generic-param-attrs.rs new file mode 100644 index 00000000000..37fabcd7e1e --- /dev/null +++ b/src/test/ui/generic-param-attrs.rs @@ -0,0 +1,54 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This test previously ensured that attributes on formals in generic parameter +// lists are rejected without a feature gate. +// +// (We are prefixing all tested features with `rustc_`, to ensure that +// the attributes themselves won't be rejected by the compiler when +// using `rustc_attrs` feature. There is a separate compile-fail/ test +// ensuring that the attribute feature-gating works in this context.) + +// must-compile-successfully + +#![feature(rustc_attrs)] +#![allow(dead_code)] + +struct StLt<#[rustc_lt_struct] 'a>(&'a u32); +struct StTy<#[rustc_ty_struct] I>(I); +enum EnLt<#[rustc_lt_enum] 'b> { A(&'b u32), B } +enum EnTy<#[rustc_ty_enum] J> { A(J), B } +trait TrLt<#[rustc_lt_trait] 'c> { fn foo(&self, _: &'c [u32]) -> &'c u32; } +trait TrTy<#[rustc_ty_trait] K> { fn foo(&self, _: K); } +type TyLt<#[rustc_lt_type] 'd> = &'d u32; +type TyTy<#[rustc_ty_type] L> = (L, ); + +impl<#[rustc_lt_inherent] 'e> StLt<'e> { } +impl<#[rustc_ty_inherent] M> StTy { } +impl<#[rustc_lt_impl_for] 'f> TrLt<'f> for StLt<'f> { + fn foo(&self, _: &'f [u32]) -> &'f u32 { loop { } } +} +impl<#[rustc_ty_impl_for] N> TrTy for StTy { + fn foo(&self, _: N) { } +} + +fn f_lt<#[rustc_lt_fn] 'g>(_: &'g [u32]) -> &'g u32 { loop { } } +fn f_ty<#[rustc_ty_fn] O>(_: O) { } + +impl StTy { + fn m_lt<#[rustc_lt_meth] 'h>(_: &'h [u32]) -> &'h u32 { loop { } } + fn m_ty<#[rustc_ty_meth] P>(_: P) { } +} + +fn hof_lt(_: Q) + where Q: for <#[rustc_lt_hof] 'i> Fn(&'i [u32]) -> &'i u32 +{} + +fn main() {} diff --git a/src/test/ui/nll/drop-may-dangle.rs b/src/test/ui/nll/drop-may-dangle.rs index 2780b347463..55c9f5de302 100644 --- a/src/test/ui/nll/drop-may-dangle.rs +++ b/src/test/ui/nll/drop-may-dangle.rs @@ -17,7 +17,6 @@ #![allow(warnings)] #![feature(dropck_eyepatch)] -#![feature(generic_param_attrs)] fn use_x(_: usize) -> bool { true } diff --git a/src/test/ui/nll/drop-no-may-dangle.rs b/src/test/ui/nll/drop-no-may-dangle.rs index 3d9a5456cbb..e5478e39fec 100644 --- a/src/test/ui/nll/drop-no-may-dangle.rs +++ b/src/test/ui/nll/drop-no-may-dangle.rs @@ -17,7 +17,6 @@ #![allow(warnings)] #![feature(dropck_eyepatch)] -#![feature(generic_param_attrs)] fn use_x(_: usize) -> bool { true } diff --git a/src/test/ui/nll/drop-no-may-dangle.stderr b/src/test/ui/nll/drop-no-may-dangle.stderr index 6454413901b..a35271bdcfe 100644 --- a/src/test/ui/nll/drop-no-may-dangle.stderr +++ b/src/test/ui/nll/drop-no-may-dangle.stderr @@ -1,5 +1,5 @@ error[E0506]: cannot assign to `v[..]` because it is borrowed - --> $DIR/drop-no-may-dangle.rs:31:9 + --> $DIR/drop-no-may-dangle.rs:30:9 | LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; | ----- borrow of `v[..]` occurs here @@ -11,7 +11,7 @@ LL | } | - borrow later used here, when `p` is dropped error[E0506]: cannot assign to `v[..]` because it is borrowed - --> $DIR/drop-no-may-dangle.rs:34:5 + --> $DIR/drop-no-may-dangle.rs:33:5 | LL | let p: WrapMayNotDangle<&usize> = WrapMayNotDangle { value: &v[0] }; | ----- borrow of `v[..]` occurs here -- cgit 1.4.1-3-g733a5 From 8958815916201421b0a6648c68d7eb31bd3197ee Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 4 Apr 2018 07:16:25 -0700 Subject: Bump the bootstrap compiler to 1.26.0 beta Holy cow that's a lot of `cfg(stage0)` removed and a lot of new stable language features! --- src/bootstrap/lib.rs | 5 ++- src/bootstrap/tool.rs | 1 - src/liballoc/benches/lib.rs | 1 - src/liballoc/lib.rs | 3 +- src/liballoc/tests/lib.rs | 1 - src/libcore/cmp.rs | 5 ++- src/libcore/intrinsics.rs | 7 ---- src/libcore/lib.rs | 5 --- src/libcore/macros.rs | 65 ------------------------------------- src/libcore/panicking.rs | 3 +- src/libcore/tests/lib.rs | 3 -- src/libpanic_unwind/gcc.rs | 3 +- src/libpanic_unwind/lib.rs | 3 +- src/libpanic_unwind/seh64_gnu.rs | 3 +- src/libpanic_unwind/windows.rs | 9 ++--- src/libproc_macro/lib.rs | 1 - src/librustc/lib.rs | 7 ---- src/librustc_apfloat/lib.rs | 4 --- src/librustc_apfloat/tests/ieee.rs | 2 -- src/librustc_borrowck/lib.rs | 1 - src/librustc_const_math/lib.rs | 2 -- src/librustc_data_structures/lib.rs | 4 --- src/librustc_errors/lib.rs | 2 -- src/librustc_incremental/lib.rs | 3 -- src/librustc_lint/lib.rs | 2 -- src/librustc_metadata/lib.rs | 2 -- src/librustc_mir/lib.rs | 6 ---- src/librustc_traits/lib.rs | 2 -- src/librustc_trans/lib.rs | 4 --- src/librustc_trans_utils/lib.rs | 2 -- src/librustc_typeck/lib.rs | 6 ---- src/librustdoc/lib.rs | 1 - src/libserialize/lib.rs | 1 - src/libstd/lib.rs | 4 --- src/libstd/panicking.rs | 6 ++-- src/libsyntax/lib.rs | 2 -- src/libsyntax_pos/lib.rs | 1 - src/libunwind/libunwind.rs | 9 ++--- src/stage0.txt | 2 +- 39 files changed, 18 insertions(+), 175 deletions(-) (limited to 'src/liballoc') diff --git a/src/bootstrap/lib.rs b/src/bootstrap/lib.rs index 2eeb2691eae..6c46f3e58cf 100644 --- a/src/bootstrap/lib.rs +++ b/src/bootstrap/lib.rs @@ -114,8 +114,7 @@ //! also check out the `src/bootstrap/README.md` file for more information. #![deny(warnings)] -#![feature(conservative_impl_trait, fs_read_write, core_intrinsics)] -#![feature(slice_concat_ext)] +#![feature(core_intrinsics)] #[macro_use] extern crate build_helper; @@ -1149,7 +1148,7 @@ impl Build { fn read(&self, path: &Path) -> String { if self.config.dry_run { return String::new(); } - t!(fs::read_string(path)) + t!(fs::read_to_string(path)) } fn create_dir(&self, dir: &Path) { diff --git a/src/bootstrap/tool.rs b/src/bootstrap/tool.rs index 93b6153fcb2..5fc92611e65 100644 --- a/src/bootstrap/tool.rs +++ b/src/bootstrap/tool.rs @@ -12,7 +12,6 @@ use std::fs; use std::env; use std::path::PathBuf; use std::process::{Command, exit}; -use std::slice::SliceConcatExt; use Mode; use Compiler; diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index a43aadfe9a2..4d92fc67b2a 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -10,7 +10,6 @@ #![deny(warnings)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(rand)] #![feature(repr_simd)] #![feature(slice_sort_by_cached_key)] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 6ce2547ef6e..da26e7c852c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -97,8 +97,6 @@ #![feature(fmt_internals)] #![feature(from_ref)] #![feature(fundamental)] -#![cfg_attr(stage0, feature(generic_param_attrs))] -#![cfg_attr(stage0, feature(i128_type))] #![feature(lang_items)] #![feature(needs_allocator)] #![feature(nonzero)] @@ -123,6 +121,7 @@ #![feature(exact_chunks)] #![feature(pointer_methods)] #![feature(inclusive_range_fields)] +#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(not(test), feature(fn_traits, swap_with_slice, i128))] #![cfg_attr(test, feature(test))] diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 1a49fb9964a..a173ef10a81 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -14,7 +14,6 @@ #![feature(alloc_system)] #![feature(attr_literals)] #![feature(box_syntax)] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(const_fn)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 67445daa436..3ae9b05b865 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -427,7 +427,7 @@ impl Ord for Reverse { /// } /// } /// ``` -#[cfg_attr(not(stage0), lang = "ord")] +#[lang = "ord"] #[stable(feature = "rust1", since = "1.0.0")] pub trait Ord: Eq + PartialOrd { /// This method returns an `Ordering` between `self` and `other`. @@ -597,8 +597,7 @@ impl PartialOrd for Ordering { /// assert_eq!(x < y, true); /// assert_eq!(x.lt(&y), true); /// ``` -#[cfg_attr(stage0, lang = "ord")] -#[cfg_attr(not(stage0), lang = "partial_ord")] +#[lang = "partial_ord"] #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] pub trait PartialOrd: PartialEq { diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 3b740adc468..83274682250 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1293,7 +1293,6 @@ extern "rust-intrinsic" { pub fn bswap(x: T) -> T; /// Reverses the bits in an integer type `T`. - #[cfg(not(stage0))] pub fn bitreverse(x: T) -> T; /// Performs checked integer addition. @@ -1316,7 +1315,6 @@ extern "rust-intrinsic" { /// Performs an exact division, resulting in undefined behavior where /// `x % y != 0` or `y == 0` or `x == T::min_value() && y == -1` - #[cfg(not(stage0))] pub fn exact_div(x: T, y: T) -> T; /// Performs an unchecked division, resulting in undefined behavior @@ -1401,8 +1399,3 @@ extern "rust-intrinsic" { /// Probably will never become stable. pub fn nontemporal_store(ptr: *mut T, val: T); } - -#[cfg(stage0)] -pub unsafe fn exact_div(a: T, b: T) -> T { - unchecked_div(a, b) -} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5a62b8438f9..cf9abb26d3e 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -78,8 +78,6 @@ #![feature(doc_spotlight)] #![feature(fn_must_use)] #![feature(fundamental)] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(intrinsics)] #![feature(iterator_flatten)] #![feature(iterator_repeat_with)] @@ -103,9 +101,6 @@ #![feature(untagged_unions)] #![feature(unwind_attributes)] -#![cfg_attr(stage0, allow(unused_attributes))] -#![cfg_attr(stage0, feature(never_type))] - #[prelude_import] #[allow(unused)] use prelude::v1::*; diff --git a/src/libcore/macros.rs b/src/libcore/macros.rs index 8a87bea71e2..90a9cb3379b 100644 --- a/src/libcore/macros.rs +++ b/src/libcore/macros.rs @@ -28,71 +28,6 @@ macro_rules! panic { }); } -/// Ensure that a boolean expression is `true` at runtime. -/// -/// This will invoke the [`panic!`] macro if the provided expression cannot be -/// evaluated to `true` at runtime. -/// -/// # Uses -/// -/// Assertions are always checked in both debug and release builds, and cannot -/// be disabled. See [`debug_assert!`] for assertions that are not enabled in -/// release builds by default. -/// -/// Unsafe code relies on `assert!` to enforce run-time invariants that, if -/// violated could lead to unsafety. -/// -/// Other use-cases of `assert!` include [testing] and enforcing run-time -/// invariants in safe code (whose violation cannot result in unsafety). -/// -/// # Custom Messages -/// -/// This macro has a second form, where a custom panic message can -/// be provided with or without arguments for formatting. See [`std::fmt`] -/// for syntax for this form. -/// -/// [`panic!`]: macro.panic.html -/// [`debug_assert!`]: macro.debug_assert.html -/// [testing]: ../book/second-edition/ch11-01-writing-tests.html#checking-results-with-the-assert-macro -/// [`std::fmt`]: ../std/fmt/index.html -/// -/// # Examples -/// -/// ``` -/// // the panic message for these assertions is the stringified value of the -/// // expression given. -/// assert!(true); -/// -/// fn some_computation() -> bool { true } // a very simple function -/// -/// assert!(some_computation()); -/// -/// // assert with a custom message -/// let x = true; -/// assert!(x, "x wasn't true!"); -/// -/// let a = 3; let b = 27; -/// assert!(a + b == 30, "a = {}, b = {}", a, b); -/// ``` -#[macro_export] -#[stable(feature = "rust1", since = "1.0.0")] -#[cfg(stage0)] -macro_rules! assert { - ($cond:expr) => ( - if !$cond { - panic!(concat!("assertion failed: ", stringify!($cond))) - } - ); - ($cond:expr,) => ( - assert!($cond) - ); - ($cond:expr, $($arg:tt)+) => ( - if !$cond { - panic!($($arg)+) - } - ); -} - /// Asserts that two expressions are equal to each other (using [`PartialEq`]). /// /// On panic, this macro will print the values of the expressions with their diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 94db0baa3f9..6b3dc75af46 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -64,8 +64,7 @@ pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) #[allow(improper_ctypes)] extern { #[lang = "panic_fmt"] - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !; } let (file, line, col) = *file_line_col; diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index de7211e718c..971759dcdd0 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -23,10 +23,7 @@ #![feature(fmt_internals)] #![feature(hashmap_internals)] #![feature(iterator_step_by)] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(iterator_flatten)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(iterator_repeat_with)] #![feature(nonzero)] #![feature(pattern)] diff --git a/src/libpanic_unwind/gcc.rs b/src/libpanic_unwind/gcc.rs index ca2fd561cad..eb6dc5b5488 100644 --- a/src/libpanic_unwind/gcc.rs +++ b/src/libpanic_unwind/gcc.rs @@ -286,8 +286,7 @@ unsafe fn find_eh_action(context: *mut uw::_Unwind_Context) // See docs in the `unwind` module. #[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] #[lang = "eh_unwind_resume"] -#[cfg_attr(stage0, unwind)] -#[cfg_attr(not(stage0), unwind(allowed))] +#[unwind(allowed)] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: *mut u8) -> ! { uw::_Unwind_Resume(panic_ctx as *mut uw::_Unwind_Exception); } diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index a5cebc3e4d0..a5c227cb401 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -112,8 +112,7 @@ pub unsafe extern "C" fn __rust_maybe_catch_panic(f: fn(*mut u8), // Entry point for raising an exception, just delegates to the platform-specific // implementation. #[no_mangle] -#[cfg_attr(stage0, unwind)] -#[cfg_attr(not(stage0), unwind(allowed))] +#[unwind(allowed)] pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 { imp::panic(mem::transmute(raw::TraitObject { data: data as *mut (), diff --git a/src/libpanic_unwind/seh64_gnu.rs b/src/libpanic_unwind/seh64_gnu.rs index 090cd095380..c3715f96c64 100644 --- a/src/libpanic_unwind/seh64_gnu.rs +++ b/src/libpanic_unwind/seh64_gnu.rs @@ -108,8 +108,7 @@ unsafe extern "C" fn rust_eh_personality(exceptionRecord: *mut c::EXCEPTION_RECO } #[lang = "eh_unwind_resume"] -#[cfg_attr(stage0, unwind)] -#[cfg_attr(not(stage0), unwind(allowed))] +#[unwind(allowed)] unsafe extern "C" fn rust_eh_unwind_resume(panic_ctx: c::LPVOID) -> ! { let params = [panic_ctx as c::ULONG_PTR]; c::RaiseException(RUST_PANIC, diff --git a/src/libpanic_unwind/windows.rs b/src/libpanic_unwind/windows.rs index 50fba5faee7..5f1dda36a88 100644 --- a/src/libpanic_unwind/windows.rs +++ b/src/libpanic_unwind/windows.rs @@ -79,21 +79,18 @@ pub enum EXCEPTION_DISPOSITION { pub use self::EXCEPTION_DISPOSITION::*; extern "system" { - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn RaiseException(dwExceptionCode: DWORD, dwExceptionFlags: DWORD, nNumberOfArguments: DWORD, lpArguments: *const ULONG_PTR); - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn RtlUnwindEx(TargetFrame: LPVOID, TargetIp: LPVOID, ExceptionRecord: *const EXCEPTION_RECORD, ReturnValue: LPVOID, OriginalContext: *const CONTEXT, HistoryTable: *const UNWIND_HISTORY_TABLE); - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn _CxxThrowException(pExceptionObject: *mut c_void, pThrowInfo: *mut u8); } diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 007093981d3..6b2b68b1faa 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -34,7 +34,6 @@ test(no_crate_inject, attr(deny(warnings))), test(attr(allow(dead_code, deprecated, unused_variables, unused_mut))))] -#![cfg_attr(stage0, feature(i128_type))] #![feature(rustc_private)] #![feature(staged_api)] #![feature(lang_items)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index dcad8132c2b..7da664e6d02 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -43,19 +43,14 @@ #![feature(box_patterns)] #![feature(box_syntax)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(const_fn)] -#![cfg_attr(stage0, feature(copy_closures, clone_closures))] #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(dyn_trait)] #![feature(entry_or_default)] #![feature(from_ref)] #![feature(fs_read_write)] -#![cfg_attr(stage0, feature(i128_type, i128))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![cfg_attr(windows, feature(libc))] -#![cfg_attr(stage0, feature(match_default_bindings))] #![feature(macro_lifetime_matcher)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] @@ -68,8 +63,6 @@ #![feature(slice_patterns)] #![feature(specialization)] #![feature(unboxed_closures)] -#![cfg_attr(stage0, feature(underscore_lifetimes))] -#![cfg_attr(stage0, feature(universal_impl_trait))] #![feature(trace_macros)] #![feature(trusted_len)] #![feature(catch_expr)] diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 6f08fcf7025..276f6cd09bf 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -46,10 +46,6 @@ #![deny(warnings)] #![forbid(unsafe_code)] -#![cfg_attr(stage0, feature(slice_patterns))] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(try_from))] - // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. #[allow(unused_extern_crates)] extern crate rustc_cratesio_shim; diff --git a/src/librustc_apfloat/tests/ieee.rs b/src/librustc_apfloat/tests/ieee.rs index 627d79724b2..6e06ea858ef 100644 --- a/src/librustc_apfloat/tests/ieee.rs +++ b/src/librustc_apfloat/tests/ieee.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![cfg_attr(stage0, feature(i128_type))] - #[macro_use] extern crate rustc_apfloat; diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index d54654c6086..6fe2ac2b0ca 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -16,7 +16,6 @@ #![allow(non_camel_case_types)] #![feature(from_ref)] -#![cfg_attr(stage0, feature(match_default_bindings))] #![feature(quote)] #[macro_use] extern crate log; diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index 7177e2818fb..c4c5886d465 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -19,8 +19,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] -#![cfg_attr(stage0, feature(i128_type, i128))] - extern crate rustc_apfloat; extern crate syntax; diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 622fb423b51..1e1628936d5 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -26,14 +26,10 @@ #![feature(unboxed_closures)] #![feature(fn_traits)] #![feature(unsize)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] -#![cfg_attr(stage0, feature(i128_type, i128))] #![feature(specialization)] #![feature(optin_builtin_traits)] -#![cfg_attr(stage0, feature(underscore_lifetimes))] #![feature(macro_vis_matcher)] #![feature(allow_internal_unstable)] -#![cfg_attr(stage0, feature(universal_impl_trait))] #![cfg_attr(unix, feature(libc))] #![cfg_attr(test, feature(test))] diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index 37ae64cef57..c283df6ec0f 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -17,8 +17,6 @@ #![allow(unused_attributes)] #![feature(range_contains)] #![cfg_attr(unix, feature(libc))] -#![cfg_attr(stage0, feature(conservative_impl_trait))] -#![cfg_attr(stage0, feature(i128_type))] #![feature(optin_builtin_traits)] extern crate atty; diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index cad72ff778b..9e72ede309d 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -15,10 +15,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![deny(warnings)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(fs_read_write)] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(specialization)] extern crate graphviz; diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index d024adad9d0..c915181213d 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -27,11 +27,9 @@ #![cfg_attr(test, feature(test))] #![feature(box_patterns)] #![feature(box_syntax)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(macro_vis_matcher)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![cfg_attr(stage0, feature(never_type))] #[macro_use] extern crate syntax; diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 4af5ec9ae08..e89b5a7fc1b 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -14,9 +14,7 @@ #![deny(warnings)] #![feature(box_patterns)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(fs_read_write)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(libc)] #![feature(macro_lifetime_matcher)] #![feature(proc_macro_internals)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 84baa8c5417..8762e7550cd 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -21,22 +21,16 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(box_patterns)] #![feature(box_syntax)] #![feature(catch_expr)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] #![feature(dyn_trait)] #![feature(fs_read_write)] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(macro_vis_matcher)] -#![cfg_attr(stage0, feature(match_default_bindings))] #![feature(exhaustive_patterns)] #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] #![feature(nonzero)] -#![cfg_attr(stage0, feature(underscore_lifetimes))] -#![cfg_attr(stage0, feature(never_type))] #![feature(inclusive_range_fields)] extern crate arena; diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index 90f368edeec..cfa3b6912f2 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -14,8 +14,6 @@ #![deny(warnings)] #![feature(crate_visibility_modifier)] -#![cfg_attr(stage0, feature(match_default_bindings))] -#![cfg_attr(stage0, feature(underscore_lifetimes))] #[macro_use] extern crate log; diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index e8a1eb3071a..2ce13a2627f 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -24,13 +24,9 @@ #![feature(custom_attribute)] #![feature(fs_read_write)] #![allow(unused_attributes)] -#![cfg_attr(stage0, feature(i128_type, i128))] -#![cfg_attr(stage0, feature(inclusive_range_syntax))] #![feature(libc)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![cfg_attr(stage0, feature(slice_patterns))] -#![cfg_attr(stage0, feature(conservative_impl_trait))] #![feature(optin_builtin_traits)] #![feature(inclusive_range_fields)] diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index 99de124c6e1..cf47d9b62a9 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -21,10 +21,8 @@ #![feature(box_syntax)] #![feature(custom_attribute)] #![allow(unused_attributes)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(quote)] #![feature(rustc_diagnostic_macros)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] extern crate ar; extern crate flate2; diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 44ecb32a0bf..6f71db998bd 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -72,22 +72,16 @@ This API is completely unstable and subject to change. #![allow(non_camel_case_types)] -#![cfg_attr(stage0, feature(advanced_slice_patterns))] #![feature(box_patterns)] #![feature(box_syntax)] -#![cfg_attr(stage0, feature(conservative_impl_trait))] -#![cfg_attr(stage0, feature(copy_closures, clone_closures))] #![feature(crate_visibility_modifier)] #![feature(from_ref)] -#![cfg_attr(stage0, feature(match_default_bindings))] #![feature(exhaustive_patterns)] #![feature(option_filter)] #![feature(quote)] #![feature(refcell_replace_swap)] #![feature(rustc_diagnostic_macros)] #![feature(slice_patterns)] -#![cfg_attr(stage0, feature(i128_type))] -#![cfg_attr(stage0, feature(never_type))] #![feature(dyn_trait)] #[macro_use] extern crate log; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index e31390f59e2..42e87f88fd4 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -20,7 +20,6 @@ #![feature(box_syntax)] #![feature(fs_read_write)] #![feature(set_stdio)] -#![cfg_attr(stage0, feature(slice_patterns))] #![feature(test)] #![feature(unicode)] #![feature(vec_remove_item)] diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index ee952523462..f78eed30694 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -23,7 +23,6 @@ Core encoding and decoding interfaces. #![feature(box_syntax)] #![feature(core_intrinsics)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(specialization)] #![cfg_attr(test, feature(test))] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3f1fec4c317..7da2eeefaaa 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -269,7 +269,6 @@ #![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(hashmap_internals)] #![feature(heap_api)] -#![cfg_attr(stage0, feature(i128_type, i128))] #![feature(int_error_internals)] #![feature(integer_atomics)] #![feature(into_cow)] @@ -321,8 +320,6 @@ #![feature(doc_spotlight)] #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] -#![cfg_attr(stage0, feature(never_type))] -#![cfg_attr(stage0, feature(termination_trait))] #![default_lib_allocator] @@ -355,7 +352,6 @@ use prelude::v1::*; // add a new crate name so we can attach the re-exports to it. #[macro_reexport(assert_eq, assert_ne, debug_assert, debug_assert_eq, debug_assert_ne, unreachable, unimplemented, write, writeln, try)] -#[cfg_attr(stage0, macro_reexport(assert))] extern crate core as __core; #[macro_use] diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 454ac64735c..fba3269204e 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -55,8 +55,7 @@ extern { data: *mut u8, data_ptr: *mut usize, vtable_ptr: *mut usize) -> u32; - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] fn __rust_start_panic(data: usize, vtable: usize) -> u32; } @@ -316,8 +315,7 @@ pub fn panicking() -> bool { /// Entry point of panic from the libcore crate. #[cfg(not(test))] #[lang = "panic_fmt"] -#[cfg_attr(stage0, unwind)] -#[cfg_attr(not(stage0), unwind(allowed))] +#[unwind(allowed)] pub extern fn rust_begin_panic(msg: fmt::Arguments, file: &'static str, line: u32, diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index dc349c1a3e6..c456dc45d21 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -22,9 +22,7 @@ #![feature(unicode)] #![feature(rustc_diagnostic_macros)] -#![cfg_attr(stage0, feature(match_default_bindings))] #![feature(non_exhaustive)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(const_atomic_usize_new)] #![feature(rustc_attrs)] diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index eb345200f41..b6315900485 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -21,7 +21,6 @@ #![feature(const_fn)] #![feature(custom_attribute)] -#![cfg_attr(stage0, feature(i128_type))] #![feature(optin_builtin_traits)] #![allow(unused_attributes)] #![feature(specialization)] diff --git a/src/libunwind/libunwind.rs b/src/libunwind/libunwind.rs index aa73b11fb38..a640a2b7775 100644 --- a/src/libunwind/libunwind.rs +++ b/src/libunwind/libunwind.rs @@ -83,8 +83,7 @@ pub enum _Unwind_Context {} pub type _Unwind_Exception_Cleanup_Fn = extern "C" fn(unwind_code: _Unwind_Reason_Code, exception: *mut _Unwind_Exception); extern "C" { - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn _Unwind_Resume(exception: *mut _Unwind_Exception) -> !; pub fn _Unwind_DeleteException(exception: *mut _Unwind_Exception); pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> *mut c_void; @@ -221,8 +220,7 @@ if #[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))] { if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { // Not 32-bit iOS extern "C" { - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn _Unwind_RaiseException(exception: *mut _Unwind_Exception) -> _Unwind_Reason_Code; pub fn _Unwind_Backtrace(trace: _Unwind_Trace_Fn, trace_argument: *mut c_void) @@ -231,8 +229,7 @@ if #[cfg(not(all(target_os = "ios", target_arch = "arm")))] { } else { // 32-bit iOS uses SjLj and does not provide _Unwind_Backtrace() extern "C" { - #[cfg_attr(stage0, unwind)] - #[cfg_attr(not(stage0), unwind(allowed))] + #[unwind(allowed)] pub fn _Unwind_SjLj_RaiseException(e: *mut _Unwind_Exception) -> _Unwind_Reason_Code; } diff --git a/src/stage0.txt b/src/stage0.txt index 96ec1e6834d..e8db3358cf0 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2018-03-18 +date: 2018-04-04 rustc: beta cargo: beta -- cgit 1.4.1-3-g733a5 From a29d4d9ad6fb27003712932566724be265e354cd Mon Sep 17 00:00:00 2001 From: Wim Looman Date: Thu, 5 Apr 2018 20:03:02 +0200 Subject: impl Unpin for PinBox --- src/liballoc/boxed.rs | 3 +++ 1 file changed, 3 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index e59a6e9fdea..a319fb797f8 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -994,3 +994,6 @@ impl fmt::Pointer for PinBox { #[unstable(feature = "pin", issue = "49150")] impl, U: ?Sized> CoerceUnsized> for PinBox {} + +#[unstable(feature = "pin", issue = "49150")] +unsafe impl Unpin for PinBox {} -- cgit 1.4.1-3-g733a5 From 679657b863c2a53a3052d8af9defbce48e12db10 Mon Sep 17 00:00:00 2001 From: Oliver Schneider Date: Fri, 30 Mar 2018 13:06:34 +0200 Subject: Inject the `compiler_builtins` crate whenever the `core` crate is injected --- src/Cargo.lock | 14 +++++++++++++ src/liballoc/Cargo.toml | 1 + src/liballoc_jemalloc/Cargo.toml | 1 + src/liballoc_system/Cargo.toml | 1 + src/libpanic_abort/Cargo.toml | 1 + src/libpanic_unwind/Cargo.toml | 1 + src/libprofiler_builtins/Cargo.toml | 1 + src/librustc_asan/Cargo.toml | 1 + src/librustc_lsan/Cargo.toml | 1 + src/librustc_msan/Cargo.toml | 1 + src/librustc_tsan/Cargo.toml | 1 + src/libstd/lib.rs | 1 + src/libstd_unicode/Cargo.toml | 1 + src/libsyntax/print/pprust.rs | 2 +- src/libsyntax/std_inject.rs | 41 +++++++++++++++++++++++-------------- src/libunwind/Cargo.toml | 1 + src/rustc/dlmalloc_shim/Cargo.toml | 1 + src/rustc/libc_shim/Cargo.toml | 2 ++ 18 files changed, 57 insertions(+), 16 deletions(-) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index f70fc81829f..004d1c0ffc9 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -10,6 +10,7 @@ dependencies = [ name = "alloc" version = "0.0.0" dependencies = [ + "compiler_builtins 0.0.0", "core 0.0.0", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", "std_unicode 0.0.0", @@ -23,6 +24,7 @@ dependencies = [ "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", "libc 0.0.0", ] @@ -32,6 +34,7 @@ name = "alloc_system" version = "0.0.0" dependencies = [ "alloc 0.0.0", + "compiler_builtins 0.0.0", "core 0.0.0", "dlmalloc 0.0.0", "libc 0.0.0", @@ -541,6 +544,7 @@ name = "dlmalloc" version = "0.0.0" dependencies = [ "alloc 0.0.0", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -976,6 +980,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" name = "libc" version = "0.0.0" dependencies = [ + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -1254,6 +1259,7 @@ dependencies = [ name = "panic_abort" version = "0.0.0" dependencies = [ + "compiler_builtins 0.0.0", "core 0.0.0", "libc 0.0.0", ] @@ -1263,6 +1269,7 @@ name = "panic_unwind" version = "0.0.0" dependencies = [ "alloc 0.0.0", + "compiler_builtins 0.0.0", "core 0.0.0", "libc 0.0.0", "unwind 0.0.0", @@ -1401,6 +1408,7 @@ name = "profiler_builtins" version = "0.0.0" dependencies = [ "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -1797,6 +1805,7 @@ dependencies = [ "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -1942,6 +1951,7 @@ dependencies = [ "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -1991,6 +2001,7 @@ dependencies = [ "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -2130,6 +2141,7 @@ dependencies = [ "alloc_system 0.0.0", "build_helper 0.1.0", "cmake 0.1.29 (registry+https://github.com/rust-lang/crates.io-index)", + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -2343,6 +2355,7 @@ dependencies = [ name = "std_unicode" version = "0.0.0" dependencies = [ + "compiler_builtins 0.0.0", "core 0.0.0", ] @@ -2725,6 +2738,7 @@ dependencies = [ name = "unwind" version = "0.0.0" dependencies = [ + "compiler_builtins 0.0.0", "core 0.0.0", "libc 0.0.0", ] diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 3bf919b0c00..2eb8ea12604 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -10,6 +10,7 @@ path = "lib.rs" [dependencies] core = { path = "../libcore" } std_unicode = { path = "../libstd_unicode" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [dev-dependencies] rand = "0.4" diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 6d7d83dd993..fd4a4553046 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -16,6 +16,7 @@ alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [build-dependencies] build_helper = { path = "../build_helper" } diff --git a/src/liballoc_system/Cargo.toml b/src/liballoc_system/Cargo.toml index f9a57f7d97a..936e20a32e1 100644 --- a/src/liballoc_system/Cargo.toml +++ b/src/liballoc_system/Cargo.toml @@ -13,6 +13,7 @@ doc = false alloc = { path = "../liballoc" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } # See comments in the source for what this dependency is [target.'cfg(all(target_arch = "wasm32", not(target_os = "emscripten")))'.dependencies] diff --git a/src/libpanic_abort/Cargo.toml b/src/libpanic_abort/Cargo.toml index e0eac41f49e..633d273b3b9 100644 --- a/src/libpanic_abort/Cargo.toml +++ b/src/libpanic_abort/Cargo.toml @@ -12,3 +12,4 @@ doc = false [dependencies] core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libpanic_unwind/Cargo.toml b/src/libpanic_unwind/Cargo.toml index a978ea16e9e..74aaa4d5ae3 100644 --- a/src/libpanic_unwind/Cargo.toml +++ b/src/libpanic_unwind/Cargo.toml @@ -14,3 +14,4 @@ alloc = { path = "../liballoc" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } unwind = { path = "../libunwind" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libprofiler_builtins/Cargo.toml b/src/libprofiler_builtins/Cargo.toml index 04f456917b9..79192fbb681 100644 --- a/src/libprofiler_builtins/Cargo.toml +++ b/src/libprofiler_builtins/Cargo.toml @@ -13,6 +13,7 @@ doc = false [dependencies] core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [build-dependencies] cc = "1.0.1" diff --git a/src/librustc_asan/Cargo.toml b/src/librustc_asan/Cargo.toml index 8f8ef1cc4a0..34d8b75a5bf 100644 --- a/src/librustc_asan/Cargo.toml +++ b/src/librustc_asan/Cargo.toml @@ -17,3 +17,4 @@ cmake = "0.1.18" alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/librustc_lsan/Cargo.toml b/src/librustc_lsan/Cargo.toml index 087c3162119..9c19b537426 100644 --- a/src/librustc_lsan/Cargo.toml +++ b/src/librustc_lsan/Cargo.toml @@ -17,3 +17,4 @@ cmake = "0.1.18" alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/librustc_msan/Cargo.toml b/src/librustc_msan/Cargo.toml index 8d7279b29eb..17ec2b96438 100644 --- a/src/librustc_msan/Cargo.toml +++ b/src/librustc_msan/Cargo.toml @@ -17,3 +17,4 @@ cmake = "0.1.18" alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/librustc_tsan/Cargo.toml b/src/librustc_tsan/Cargo.toml index 7b83985ba67..8bb67c0bbac 100644 --- a/src/librustc_tsan/Cargo.toml +++ b/src/librustc_tsan/Cargo.toml @@ -17,3 +17,4 @@ cmake = "0.1.18" alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 6f6abfdd31e..f9041ac8546 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -373,6 +373,7 @@ extern crate unwind; // compiler-rt intrinsics #[doc(masked)] +#[cfg(stage0)] extern crate compiler_builtins; // During testing, this crate is not actually the "real" std library, but rather diff --git a/src/libstd_unicode/Cargo.toml b/src/libstd_unicode/Cargo.toml index b3346dbe2fb..283070a0e2c 100644 --- a/src/libstd_unicode/Cargo.toml +++ b/src/libstd_unicode/Cargo.toml @@ -15,3 +15,4 @@ path = "tests/lib.rs" [dependencies] core = { path = "../libcore" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libsyntax/print/pprust.rs b/src/libsyntax/print/pprust.rs index 8d42206c5cc..8168db19058 100644 --- a/src/libsyntax/print/pprust.rs +++ b/src/libsyntax/print/pprust.rs @@ -94,7 +94,7 @@ pub fn print_crate<'a>(cm: &'a CodeMap, is_expanded: bool) -> io::Result<()> { let mut s = State::new_from_input(cm, sess, filename, input, out, ann, is_expanded); - if is_expanded && !std_inject::injected_crate_name().is_none() { + if is_expanded && std_inject::injected_crate_name().is_some() { // We need to print `#![no_std]` (and its feature gate) so that // compiling pretty-printed source won't inject libstd again. // However we don't want these attributes in the AST because diff --git a/src/libsyntax/std_inject.rs b/src/libsyntax/std_inject.rs index 63d7b3336a8..bba7a2d7377 100644 --- a/src/libsyntax/std_inject.rs +++ b/src/libsyntax/std_inject.rs @@ -44,27 +44,38 @@ thread_local! { } pub fn maybe_inject_crates_ref(mut krate: ast::Crate, alt_std_name: Option<&str>) -> ast::Crate { - let name = if attr::contains_name(&krate.attrs, "no_core") { + // the first name in this list is the crate name of the crate with the prelude + let names: &[&str] = if attr::contains_name(&krate.attrs, "no_core") { return krate; } else if attr::contains_name(&krate.attrs, "no_std") { - "core" + if attr::contains_name(&krate.attrs, "compiler_builtins") { + &["core"] + } else { + &["core", "compiler_builtins"] + } } else { - "std" + &["std"] }; - INJECTED_CRATE_NAME.with(|opt_name| opt_name.set(Some(name))); + for name in names { + krate.module.items.insert(0, P(ast::Item { + attrs: vec![attr::mk_attr_outer(DUMMY_SP, + attr::mk_attr_id(), + attr::mk_word_item(ast::Ident::from_str("macro_use")))], + vis: dummy_spanned(ast::VisibilityKind::Inherited), + node: ast::ItemKind::ExternCrate(alt_std_name.map(Symbol::intern)), + ident: ast::Ident::from_str(name), + id: ast::DUMMY_NODE_ID, + span: DUMMY_SP, + tokens: None, + })); + } - krate.module.items.insert(0, P(ast::Item { - attrs: vec![attr::mk_attr_outer(DUMMY_SP, - attr::mk_attr_id(), - attr::mk_word_item(ast::Ident::from_str("macro_use")))], - vis: dummy_spanned(ast::VisibilityKind::Inherited), - node: ast::ItemKind::ExternCrate(alt_std_name.map(Symbol::intern)), - ident: ast::Ident::from_str(name), - id: ast::DUMMY_NODE_ID, - span: DUMMY_SP, - tokens: None, - })); + // the crates have been injected, the assumption is that the first one is the one with + // the prelude. + let name = names[0]; + + INJECTED_CRATE_NAME.with(|opt_name| opt_name.set(Some(name))); let span = ignored_span(DUMMY_SP); krate.module.items.insert(0, P(ast::Item { diff --git a/src/libunwind/Cargo.toml b/src/libunwind/Cargo.toml index fbd9789d2f5..4760461df64 100644 --- a/src/libunwind/Cargo.toml +++ b/src/libunwind/Cargo.toml @@ -14,3 +14,4 @@ doc = false [dependencies] core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } +compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/rustc/dlmalloc_shim/Cargo.toml b/src/rustc/dlmalloc_shim/Cargo.toml index cf8440c40da..d2fe159d806 100644 --- a/src/rustc/dlmalloc_shim/Cargo.toml +++ b/src/rustc/dlmalloc_shim/Cargo.toml @@ -11,4 +11,5 @@ doc = false [dependencies] core = { path = "../../libcore" } +compiler_builtins = { path = "../../rustc/compiler_builtins_shim" } alloc = { path = "../../liballoc" } diff --git a/src/rustc/libc_shim/Cargo.toml b/src/rustc/libc_shim/Cargo.toml index 0c04402124a..e77897d6433 100644 --- a/src/rustc/libc_shim/Cargo.toml +++ b/src/rustc/libc_shim/Cargo.toml @@ -29,6 +29,8 @@ doc = false # # See https://github.com/rust-lang/rfcs/pull/1133. core = { path = "../../libcore" } +compiler_builtins = { path = "../compiler_builtins_shim" } + [features] # Certain parts of libc are conditionally compiled differently than when used -- cgit 1.4.1-3-g733a5 From d4dff03e7c6decd6b545dcb290d2c592969b3d81 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 8 Apr 2018 09:03:33 +0200 Subject: Remove inline on Vec::shrink_to_fit as asked by Alex --- src/liballoc/vec.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index e3c036e6aac..3fff28469fe 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -582,7 +582,6 @@ impl Vec { /// assert!(vec.capacity() >= 3); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[inline] pub fn shrink_to_fit(&mut self) { if self.capacity() != self.len { self.buf.shrink_to_fit(self.len); -- cgit 1.4.1-3-g733a5 From c115cc655c8bb3077ff349e4ddd704a2239438a6 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Sun, 1 Apr 2018 09:35:53 -0600 Subject: Move deny(warnings) into rustbuild This permits easier iteration without having to worry about warnings being denied. Fixes #49517 --- config.toml.example | 3 +++ src/bootstrap/bin/rustc.rs | 4 ++++ src/bootstrap/builder.rs | 5 +++++ src/bootstrap/config.rs | 8 ++++++++ src/bootstrap/flags.rs | 6 ++++++ src/build_helper/lib.rs | 1 - src/liballoc/benches/lib.rs | 2 -- src/liballoc/lib.rs | 1 - src/liballoc/tests/lib.rs | 2 -- src/liballoc_jemalloc/lib.rs | 1 - src/liballoc_system/lib.rs | 1 - src/libarena/lib.rs | 1 - src/libcore/benches/lib.rs | 2 -- src/libcore/lib.rs | 1 - src/libcore/tests/lib.rs | 2 -- src/libfmt_macros/lib.rs | 1 - src/libgraphviz/lib.rs | 1 - src/libpanic_abort/lib.rs | 1 - src/libpanic_unwind/lib.rs | 1 - src/libproc_macro/lib.rs | 1 - src/librustc/benches/lib.rs | 2 -- src/librustc/lib.rs | 1 - src/librustc_allocator/lib.rs | 2 -- src/librustc_apfloat/lib.rs | 1 - src/librustc_back/lib.rs | 1 - src/librustc_borrowck/lib.rs | 1 - src/librustc_const_math/lib.rs | 1 - src/librustc_data_structures/lib.rs | 1 - src/librustc_driver/lib.rs | 1 - src/librustc_errors/lib.rs | 1 - src/librustc_incremental/lib.rs | 1 - src/librustc_lint/lib.rs | 1 - src/librustc_llvm/lib.rs | 1 - src/librustc_metadata/lib.rs | 1 - src/librustc_mir/lib.rs | 2 -- src/librustc_passes/lib.rs | 1 - src/librustc_platform_intrinsics/lib.rs | 1 - src/librustc_plugin/lib.rs | 1 - src/librustc_privacy/lib.rs | 1 - src/librustc_resolve/lib.rs | 1 - src/librustc_save_analysis/lib.rs | 1 - src/librustc_traits/lib.rs | 2 -- src/librustc_trans/lib.rs | 1 - src/librustc_trans_utils/lib.rs | 1 - src/librustc_typeck/lib.rs | 1 - src/librustdoc/lib.rs | 1 - src/libserialize/lib.rs | 1 - src/libstd/lib.rs | 4 ---- src/libstd_unicode/lib.rs | 1 - src/libsyntax/lib.rs | 1 - src/libsyntax_ext/lib.rs | 1 - src/libsyntax_pos/lib.rs | 1 - src/libterm/lib.rs | 1 - src/libtest/lib.rs | 1 - src/libunwind/lib.rs | 1 - src/tools/tidy/src/lib.rs | 2 -- 56 files changed, 26 insertions(+), 63 deletions(-) (limited to 'src/liballoc') diff --git a/config.toml.example b/config.toml.example index 9dd3002506e..68bc7dfe720 100644 --- a/config.toml.example +++ b/config.toml.example @@ -339,6 +339,9 @@ # rustc to execute. #lld = false +# Whether to deny warnings in crates +#deny-warnings = true + # ============================================================================= # Options for specific targets # diff --git a/src/bootstrap/bin/rustc.rs b/src/bootstrap/bin/rustc.rs index 6701f58ba8e..3dd9b684059 100644 --- a/src/bootstrap/bin/rustc.rs +++ b/src/bootstrap/bin/rustc.rs @@ -279,6 +279,10 @@ fn main() { cmd.arg("--color=always"); } + if env::var_os("RUSTC_DENY_WARNINGS").is_some() { + cmd.arg("-Dwarnings"); + } + if verbose > 1 { eprintln!("rustc command: {:?}", cmd); eprintln!("sysroot: {:?}", sysroot); diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index 3f5ec4933d0..7ff64af9196 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -698,6 +698,11 @@ impl<'a> Builder<'a> { cargo.env("RUSTC_VERBOSE", format!("{}", self.verbosity)); + // in std, we want to avoid denying warnings for stage 0 as that makes cfg's painful. + if self.config.deny_warnings && !(mode == Mode::Libstd && stage == 0) { + cargo.env("RUSTC_DENY_WARNINGS", "1"); + } + // Throughout the build Cargo can execute a number of build scripts // compiling C/C++ code and we need to pass compilers, archivers, flags, etc // obtained previously to those build scripts. diff --git a/src/bootstrap/config.rs b/src/bootstrap/config.rs index 95baf4f8cca..239316d45c4 100644 --- a/src/bootstrap/config.rs +++ b/src/bootstrap/config.rs @@ -71,6 +71,8 @@ pub struct Config { pub incremental: bool, pub dry_run: bool, + pub deny_warnings: bool, + // llvm codegen options pub llvm_enabled: bool, pub llvm_assertions: bool, @@ -301,6 +303,7 @@ struct Rust { codegen_backends_dir: Option, wasm_syscall: Option, lld: Option, + deny_warnings: Option, } /// TOML representation of how each build target is configured. @@ -340,6 +343,7 @@ impl Config { config.test_miri = false; config.rust_codegen_backends = vec![INTERNER.intern_str("llvm")]; config.rust_codegen_backends_dir = "codegen-backends".to_owned(); + config.deny_warnings = true; // set by bootstrap.py config.src = env::var_os("SRC").map(PathBuf::from).expect("'SRC' to be set"); @@ -366,6 +370,9 @@ impl Config { config.incremental = flags.incremental; config.dry_run = flags.dry_run; config.keep_stage = flags.keep_stage; + if let Some(value) = flags.warnings { + config.deny_warnings = value; + } if config.dry_run { let dir = config.out.join("tmp-dry-run"); @@ -511,6 +518,7 @@ impl Config { config.rustc_default_linker = rust.default_linker.clone(); config.musl_root = rust.musl_root.clone().map(PathBuf::from); config.save_toolstates = rust.save_toolstates.clone().map(PathBuf::from); + set(&mut config.deny_warnings, rust.deny_warnings.or(flags.warnings)); if let Some(ref backends) = rust.codegen_backends { config.rust_codegen_backends = backends.iter() diff --git a/src/bootstrap/flags.rs b/src/bootstrap/flags.rs index ef902c68d12..3eb9dca2aa8 100644 --- a/src/bootstrap/flags.rs +++ b/src/bootstrap/flags.rs @@ -42,6 +42,9 @@ pub struct Flags { pub exclude: Vec, pub rustc_error_format: Option, pub dry_run: bool, + + // true => deny + pub warnings: Option, } pub enum Subcommand { @@ -118,6 +121,8 @@ To learn more about a subcommand, run `./x.py -h`"); opts.optopt("", "src", "path to the root of the rust checkout", "DIR"); opts.optopt("j", "jobs", "number of jobs to run in parallel", "JOBS"); opts.optflag("h", "help", "print this help message"); + opts.optopt("", "warnings", "if value is deny, will deny warnings, otherwise use default", + "VALUE"); opts.optopt("", "error-format", "rustc error format", "FORMAT"); // fn usage() @@ -374,6 +379,7 @@ Arguments: incremental: matches.opt_present("incremental"), exclude: split(matches.opt_strs("exclude")) .into_iter().map(|p| p.into()).collect::>(), + warnings: matches.opt_str("warnings").map(|v| v == "deny"), } } } diff --git a/src/build_helper/lib.rs b/src/build_helper/lib.rs index 5a12afd03e1..e5c85ddb3a9 100644 --- a/src/build_helper/lib.rs +++ b/src/build_helper/lib.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] use std::fs::File; use std::path::{Path, PathBuf}; diff --git a/src/liballoc/benches/lib.rs b/src/liballoc/benches/lib.rs index 4d92fc67b2a..4f69aa6670b 100644 --- a/src/liballoc/benches/lib.rs +++ b/src/liballoc/benches/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(rand)] #![feature(repr_simd)] #![feature(slice_sort_by_cached_key)] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index da26e7c852c..b08bd66b47c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -72,7 +72,6 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] #![needs_allocator] -#![deny(warnings)] #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index a173ef10a81..17f1d0464a5 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(allocator_api)] #![feature(alloc_system)] #![feature(attr_literals)] diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 7a8d01e4ef8..df7e3f61f5f 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,7 +14,6 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![deny(warnings)] #![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index d4404e564e0..cdcb732f635 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -10,7 +10,6 @@ #![no_std] #![allow(unused_attributes)] -#![deny(warnings)] #![unstable(feature = "alloc_system", reason = "this library is unlikely to be stabilized in its current \ form or name", diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index 7eaf67e6ea6..b319f333342 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -22,7 +22,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(no_crate_inject, attr(deny(warnings))))] -#![deny(warnings)] #![feature(alloc)] #![feature(core_intrinsics)] diff --git a/src/libcore/benches/lib.rs b/src/libcore/benches/lib.rs index c947b003ccb..ced77d77918 100644 --- a/src/libcore/benches/lib.rs +++ b/src/libcore/benches/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(flt2dec)] #![feature(test)] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index cf9abb26d3e..e194b173aa7 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -63,7 +63,6 @@ #![no_core] #![deny(missing_docs)] #![deny(missing_debug_implementations)] -#![deny(warnings)] #![feature(allow_internal_unstable)] #![feature(asm)] diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 971759dcdd0..c3162899bbd 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(ascii_ctype)] #![feature(box_syntax)] #![feature(core_float)] diff --git a/src/libfmt_macros/lib.rs b/src/libfmt_macros/lib.rs index 0f45f965104..a551b1b770a 100644 --- a/src/libfmt_macros/lib.rs +++ b/src/libfmt_macros/lib.rs @@ -19,7 +19,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] -#![deny(warnings)] pub use self::Piece::*; pub use self::Position::*; diff --git a/src/libgraphviz/lib.rs b/src/libgraphviz/lib.rs index d8c366d2413..158d0101515 100644 --- a/src/libgraphviz/lib.rs +++ b/src/libgraphviz/lib.rs @@ -287,7 +287,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(allow(unused_variables), deny(warnings))))] -#![deny(warnings)] #![feature(str_escape)] diff --git a/src/libpanic_abort/lib.rs b/src/libpanic_abort/lib.rs index 5f768ef4399..43c5bbbc618 100644 --- a/src/libpanic_abort/lib.rs +++ b/src/libpanic_abort/lib.rs @@ -19,7 +19,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] -#![deny(warnings)] #![panic_runtime] #![allow(unused_features)] diff --git a/src/libpanic_unwind/lib.rs b/src/libpanic_unwind/lib.rs index a5c227cb401..9321d6917d1 100644 --- a/src/libpanic_unwind/lib.rs +++ b/src/libpanic_unwind/lib.rs @@ -28,7 +28,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/")] -#![deny(warnings)] #![feature(alloc)] #![feature(core_intrinsics)] diff --git a/src/libproc_macro/lib.rs b/src/libproc_macro/lib.rs index 6aa5572721d..dafdacfdd72 100644 --- a/src/libproc_macro/lib.rs +++ b/src/libproc_macro/lib.rs @@ -24,7 +24,6 @@ //! See [the book](../book/first-edition/procedural-macros.html) for more. #![stable(feature = "proc_macro_lib", since = "1.15.0")] -#![deny(warnings)] #![deny(missing_docs)] #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", diff --git a/src/librustc/benches/lib.rs b/src/librustc/benches/lib.rs index 278e0f9a26e..5496df1342f 100644 --- a/src/librustc/benches/lib.rs +++ b/src/librustc/benches/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(test)] extern crate test; diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 7da664e6d02..b54699901fa 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -39,7 +39,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_patterns)] #![feature(box_syntax)] diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index e17fce5a2ec..0c7a9a91711 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] - #![feature(rustc_private)] extern crate rustc; diff --git a/src/librustc_apfloat/lib.rs b/src/librustc_apfloat/lib.rs index 276f6cd09bf..0f051ea5981 100644 --- a/src/librustc_apfloat/lib.rs +++ b/src/librustc_apfloat/lib.rs @@ -43,7 +43,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![forbid(unsafe_code)] // See librustc_cratesio_shim/Cargo.toml for a comment explaining this. diff --git a/src/librustc_back/lib.rs b/src/librustc_back/lib.rs index 9baee267709..027a9c45555 100644 --- a/src/librustc_back/lib.rs +++ b/src/librustc_back/lib.rs @@ -24,7 +24,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_syntax)] #![feature(const_fn)] diff --git a/src/librustc_borrowck/lib.rs b/src/librustc_borrowck/lib.rs index 6fe2ac2b0ca..52a357e1a1d 100644 --- a/src/librustc_borrowck/lib.rs +++ b/src/librustc_borrowck/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![allow(non_camel_case_types)] diff --git a/src/librustc_const_math/lib.rs b/src/librustc_const_math/lib.rs index c4c5886d465..499c330be1d 100644 --- a/src/librustc_const_math/lib.rs +++ b/src/librustc_const_math/lib.rs @@ -17,7 +17,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] extern crate rustc_apfloat; diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index 1e1628936d5..ba1d73dc268 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -19,7 +19,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://www.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(collections_range)] #![feature(nonzero)] diff --git a/src/librustc_driver/lib.rs b/src/librustc_driver/lib.rs index 6f88b0aecb6..f6903d26e70 100644 --- a/src/librustc_driver/lib.rs +++ b/src/librustc_driver/lib.rs @@ -17,7 +17,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_syntax)] #![cfg_attr(unix, feature(libc))] diff --git a/src/librustc_errors/lib.rs b/src/librustc_errors/lib.rs index a723e455222..8d5f9ac93f0 100644 --- a/src/librustc_errors/lib.rs +++ b/src/librustc_errors/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(custom_attribute)] #![allow(unused_attributes)] diff --git a/src/librustc_incremental/lib.rs b/src/librustc_incremental/lib.rs index 9e72ede309d..a5e07bcec24 100644 --- a/src/librustc_incremental/lib.rs +++ b/src/librustc_incremental/lib.rs @@ -13,7 +13,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(fs_read_write)] #![feature(specialization)] diff --git a/src/librustc_lint/lib.rs b/src/librustc_lint/lib.rs index c915181213d..16e8600f2d8 100644 --- a/src/librustc_lint/lib.rs +++ b/src/librustc_lint/lib.rs @@ -22,7 +22,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![cfg_attr(test, feature(test))] #![feature(box_patterns)] diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index 16bee5b987e..bf8a087ab55 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -16,7 +16,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_syntax)] #![feature(concat_idents)] diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index e89b5a7fc1b..f02a34c65a9 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_patterns)] #![feature(fs_read_write)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index 8762e7550cd..51256c4d96f 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -14,8 +14,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! */ -#![deny(warnings)] - #![feature(slice_patterns)] #![feature(from_ref)] #![feature(box_patterns)] diff --git a/src/librustc_passes/lib.rs b/src/librustc_passes/lib.rs index 1f6cc1f71fc..e65c9de8df1 100644 --- a/src/librustc_passes/lib.rs +++ b/src/librustc_passes/lib.rs @@ -17,7 +17,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_platform_intrinsics/lib.rs b/src/librustc_platform_intrinsics/lib.rs index 4cc65ee28e8..b57debdd994 100644 --- a/src/librustc_platform_intrinsics/lib.rs +++ b/src/librustc_platform_intrinsics/lib.rs @@ -8,7 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![deny(warnings)] #![allow(bad_style)] pub struct Intrinsic { diff --git a/src/librustc_plugin/lib.rs b/src/librustc_plugin/lib.rs index c0f830f1fbe..622d8e51a6c 100644 --- a/src/librustc_plugin/lib.rs +++ b/src/librustc_plugin/lib.rs @@ -63,7 +63,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(rustc_diagnostic_macros)] #![feature(staged_api)] diff --git a/src/librustc_privacy/lib.rs b/src/librustc_privacy/lib.rs index d951a7f1cc1..ef710ff7a7e 100644 --- a/src/librustc_privacy/lib.rs +++ b/src/librustc_privacy/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_resolve/lib.rs b/src/librustc_resolve/lib.rs index 2bf17cd1317..61ff326b2af 100644 --- a/src/librustc_resolve/lib.rs +++ b/src/librustc_resolve/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 4f46fb3545b..fefedd4e1c8 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -11,7 +11,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(custom_attribute)] #![feature(macro_lifetime_matcher)] #![allow(unused_attributes)] diff --git a/src/librustc_traits/lib.rs b/src/librustc_traits/lib.rs index cfa3b6912f2..8136f6857a5 100644 --- a/src/librustc_traits/lib.rs +++ b/src/librustc_traits/lib.rs @@ -11,8 +11,6 @@ //! New recursive solver modeled on Chalk's recursive solver. Most of //! the guts are broken up into modules; see the comments in those modules. -#![deny(warnings)] - #![feature(crate_visibility_modifier)] #[macro_use] diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 344f959c141..b9fa5b1353c 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -17,7 +17,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_patterns)] #![feature(box_syntax)] diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs index cf47d9b62a9..b297fd99865 100644 --- a/src/librustc_trans_utils/lib.rs +++ b/src/librustc_trans_utils/lib.rs @@ -15,7 +15,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(box_patterns)] #![feature(box_syntax)] diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 6f71db998bd..eb9a26855c6 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -68,7 +68,6 @@ This API is completely unstable and subject to change. #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![allow(non_camel_case_types)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 42e87f88fd4..730f61e0aa6 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -12,7 +12,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/")] -#![deny(warnings)] #![feature(ascii_ctype)] #![feature(rustc_private)] diff --git a/src/libserialize/lib.rs b/src/libserialize/lib.rs index f78eed30694..22d27b6697a 100644 --- a/src/libserialize/lib.rs +++ b/src/libserialize/lib.rs @@ -19,7 +19,6 @@ Core encoding and decoding interfaces. html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/", test(attr(allow(unused_variables), deny(warnings))))] -#![deny(warnings)] #![feature(box_syntax)] #![feature(core_intrinsics)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3227aa9acff..672723341eb 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -227,10 +227,6 @@ // Tell the compiler to link to either panic_abort or panic_unwind #![needs_panic_runtime] -// Turn warnings into errors, but only after stage0, where it can be useful for -// code to emit warnings during language transitions -#![cfg_attr(not(stage0), deny(warnings))] - // std may use features in a platform-specific way #![allow(unused_features)] diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index c22ea1671fa..cf8c101a2f9 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -27,7 +27,6 @@ html_playground_url = "https://play.rust-lang.org/", issue_tracker_base_url = "https://github.com/rust-lang/rust/issues/", test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] -#![deny(warnings)] #![deny(missing_debug_implementations)] #![no_std] diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index c456dc45d21..b2976f71d49 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -18,7 +18,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![deny(warnings)] #![feature(unicode)] #![feature(rustc_diagnostic_macros)] diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index 249a64b353f..97e34c554d1 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -13,7 +13,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(proc_macro_internals)] #![feature(decl_macro)] diff --git a/src/libsyntax_pos/lib.rs b/src/libsyntax_pos/lib.rs index 33428eb271a..9a7d1fd8ee6 100644 --- a/src/libsyntax_pos/lib.rs +++ b/src/libsyntax_pos/lib.rs @@ -17,7 +17,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] #![feature(const_fn)] #![feature(custom_attribute)] diff --git a/src/libterm/lib.rs b/src/libterm/lib.rs index ad0e582b1c3..a012f4e776f 100644 --- a/src/libterm/lib.rs +++ b/src/libterm/lib.rs @@ -46,7 +46,6 @@ html_playground_url = "https://play.rust-lang.org/", test(attr(deny(warnings))))] #![deny(missing_docs)] -#![deny(warnings)] #![cfg_attr(windows, feature(libc))] // Handle rustfmt skips diff --git a/src/libtest/lib.rs b/src/libtest/lib.rs index b8be1aeff17..9291eaa910b 100644 --- a/src/libtest/lib.rs +++ b/src/libtest/lib.rs @@ -31,7 +31,6 @@ #![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![deny(warnings)] #![feature(asm)] #![feature(fnbox)] #![cfg_attr(any(unix, target_os = "cloudabi"), feature(libc))] diff --git a/src/libunwind/lib.rs b/src/libunwind/lib.rs index 5347c781218..2b3c19c067e 100644 --- a/src/libunwind/lib.rs +++ b/src/libunwind/lib.rs @@ -10,7 +10,6 @@ #![no_std] #![unstable(feature = "panic_unwind", issue = "32837")] -#![deny(warnings)] #![feature(cfg_target_vendor)] #![feature(link_cfg)] diff --git a/src/tools/tidy/src/lib.rs b/src/tools/tidy/src/lib.rs index 06eb055f68e..fa227436640 100644 --- a/src/tools/tidy/src/lib.rs +++ b/src/tools/tidy/src/lib.rs @@ -13,8 +13,6 @@ //! This library contains the tidy lints and exposes it //! to be used by tools. -#![deny(warnings)] - extern crate serde; extern crate serde_json; #[macro_use] -- cgit 1.4.1-3-g733a5 From 1aa61526a7acd4c3c2a1b2126b77502c818aa4bb Mon Sep 17 00:00:00 2001 From: varkor Date: Sat, 31 Mar 2018 21:40:46 +0100 Subject: Add trivial early return for sort_by_cached_key --- src/liballoc/slice.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 68f2313843c..56c53fca62c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1400,6 +1400,7 @@ impl [T] { let sz_usize = mem::size_of::<(K, usize)>(); let len = self.len(); + if len < 2 { return } if sz_u8 < sz_u16 && len <= ( u8::MAX as usize) { return sort_by_key!( u8, self, f) } if sz_u16 < sz_u32 && len <= (u16::MAX as usize) { return sort_by_key!(u16, self, f) } if sz_u32 < sz_usize && len <= (u32::MAX as usize) { return sort_by_key!(u32, self, f) } -- cgit 1.4.1-3-g733a5 From 54d6bcbf70112ceb0efa4579ab9f6d5673588889 Mon Sep 17 00:00:00 2001 From: Daiki Mizukami Date: Wed, 11 Apr 2018 18:24:47 +0900 Subject: alloc: Mark `Box::into_unique` with `#[doc(hidden)]` --- src/liballoc/boxed.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 71b53cc88e5..bf7921ea1a2 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -184,6 +184,7 @@ impl Box { #[unstable(feature = "ptr_internals", issue = "0", reason = "use into_raw_non_null instead")] #[inline] + #[doc(hidden)] pub fn into_unique(b: Box) -> Unique { let unique = b.0; mem::forget(b); -- cgit 1.4.1-3-g733a5 From f87d4a15a82a76e7510629173c366d084f2c02ca Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 5 Apr 2018 15:55:28 +0200 Subject: Move Utf8Lossy decoder to libcore --- src/liballoc/string.rs | 2 +- src/libcore/str/lossy.rs | 212 +++++++++++++++++++++++++++++++++++ src/libcore/str/mod.rs | 4 + src/libcore/tests/lib.rs | 2 + src/libcore/tests/str_lossy.rs | 91 +++++++++++++++ src/libstd/sys/redox/os_str.rs | 2 +- src/libstd/sys/unix/os_str.rs | 2 +- src/libstd/sys/wasm/os_str.rs | 2 +- src/libstd/sys_common/bytestring.rs | 2 +- src/libstd_unicode/Cargo.toml | 4 - src/libstd_unicode/lib.rs | 1 - src/libstd_unicode/lossy.rs | 213 ------------------------------------ src/libstd_unicode/tests/lib.rs | 15 --- src/libstd_unicode/tests/lossy.rs | 91 --------------- 14 files changed, 314 insertions(+), 329 deletions(-) create mode 100644 src/libcore/str/lossy.rs create mode 100644 src/libcore/tests/str_lossy.rs delete mode 100644 src/libstd_unicode/lossy.rs delete mode 100644 src/libstd_unicode/tests/lib.rs delete mode 100644 src/libstd_unicode/tests/lossy.rs (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index b95aae02894..5f90e28cb3c 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -63,7 +63,7 @@ use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds}; use core::ptr; use core::str::pattern::Pattern; -use std_unicode::lossy; +use core::str::lossy; use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; use borrow::{Cow, ToOwned}; diff --git a/src/libcore/str/lossy.rs b/src/libcore/str/lossy.rs new file mode 100644 index 00000000000..30b7267da7c --- /dev/null +++ b/src/libcore/str/lossy.rs @@ -0,0 +1,212 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use char; +use str as core_str; +use fmt; +use fmt::Write; +use mem; + +/// Lossy UTF-8 string. +#[unstable(feature = "str_internals", issue = "0")] +pub struct Utf8Lossy { + bytes: [u8] +} + +impl Utf8Lossy { + pub fn from_str(s: &str) -> &Utf8Lossy { + Utf8Lossy::from_bytes(s.as_bytes()) + } + + pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy { + unsafe { mem::transmute(bytes) } + } + + pub fn chunks(&self) -> Utf8LossyChunksIter { + Utf8LossyChunksIter { source: &self.bytes } + } +} + + +/// Iterator over lossy UTF-8 string +#[unstable(feature = "str_internals", issue = "0")] +#[allow(missing_debug_implementations)] +pub struct Utf8LossyChunksIter<'a> { + source: &'a [u8], +} + +#[unstable(feature = "str_internals", issue = "0")] +#[derive(PartialEq, Eq, Debug)] +pub struct Utf8LossyChunk<'a> { + /// Sequence of valid chars. + /// Can be empty between broken UTF-8 chars. + pub valid: &'a str, + /// Single broken char, empty if none. + /// Empty iff iterator item is last. + pub broken: &'a [u8], +} + +impl<'a> Iterator for Utf8LossyChunksIter<'a> { + type Item = Utf8LossyChunk<'a>; + + fn next(&mut self) -> Option> { + if self.source.len() == 0 { + return None; + } + + const TAG_CONT_U8: u8 = 128; + fn unsafe_get(xs: &[u8], i: usize) -> u8 { + unsafe { *xs.get_unchecked(i) } + } + fn safe_get(xs: &[u8], i: usize) -> u8 { + if i >= xs.len() { 0 } else { unsafe_get(xs, i) } + } + + let mut i = 0; + while i < self.source.len() { + let i_ = i; + + let byte = unsafe_get(self.source, i); + i += 1; + + if byte < 128 { + + } else { + let w = core_str::utf8_char_width(byte); + + macro_rules! error { () => ({ + unsafe { + let r = Utf8LossyChunk { + valid: core_str::from_utf8_unchecked(&self.source[0..i_]), + broken: &self.source[i_..i], + }; + self.source = &self.source[i..]; + return Some(r); + } + })} + + match w { + 2 => { + if safe_get(self.source, i) & 192 != TAG_CONT_U8 { + error!(); + } + i += 1; + } + 3 => { + match (byte, safe_get(self.source, i)) { + (0xE0, 0xA0 ... 0xBF) => (), + (0xE1 ... 0xEC, 0x80 ... 0xBF) => (), + (0xED, 0x80 ... 0x9F) => (), + (0xEE ... 0xEF, 0x80 ... 0xBF) => (), + _ => { + error!(); + } + } + i += 1; + if safe_get(self.source, i) & 192 != TAG_CONT_U8 { + error!(); + } + i += 1; + } + 4 => { + match (byte, safe_get(self.source, i)) { + (0xF0, 0x90 ... 0xBF) => (), + (0xF1 ... 0xF3, 0x80 ... 0xBF) => (), + (0xF4, 0x80 ... 0x8F) => (), + _ => { + error!(); + } + } + i += 1; + if safe_get(self.source, i) & 192 != TAG_CONT_U8 { + error!(); + } + i += 1; + if safe_get(self.source, i) & 192 != TAG_CONT_U8 { + error!(); + } + i += 1; + } + _ => { + error!(); + } + } + } + } + + let r = Utf8LossyChunk { + valid: unsafe { core_str::from_utf8_unchecked(self.source) }, + broken: &[], + }; + self.source = &[]; + return Some(r); + } +} + + +impl fmt::Display for Utf8Lossy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + // If we're the empty string then our iterator won't actually yield + // anything, so perform the formatting manually + if self.bytes.len() == 0 { + return "".fmt(f) + } + + for Utf8LossyChunk { valid, broken } in self.chunks() { + // If we successfully decoded the whole chunk as a valid string then + // we can return a direct formatting of the string which will also + // respect various formatting flags if possible. + if valid.len() == self.bytes.len() { + assert!(broken.is_empty()); + return valid.fmt(f) + } + + f.write_str(valid)?; + if !broken.is_empty() { + f.write_char(char::REPLACEMENT_CHARACTER)?; + } + } + Ok(()) + } +} + +impl fmt::Debug for Utf8Lossy { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_char('"')?; + + for Utf8LossyChunk { valid, broken } in self.chunks() { + + // Valid part. + // Here we partially parse UTF-8 again which is suboptimal. + { + let mut from = 0; + for (i, c) in valid.char_indices() { + let esc = c.escape_debug(); + // If char needs escaping, flush backlog so far and write, else skip + if esc.len() != 1 { + f.write_str(&valid[from..i])?; + for c in esc { + f.write_char(c)?; + } + from = i + c.len_utf8(); + } + } + f.write_str(&valid[from..])?; + } + + // Broken parts of string as hex escape. + for &b in broken { + write!(f, "\\x{:02x}", b)?; + } + } + + f.write_char('"') + } +} diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 1185b7acaae..7a97d89dcf9 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -26,6 +26,10 @@ use mem; pub mod pattern; +#[unstable(feature = "str_internals", issue = "0")] +#[allow(missing_docs)] +pub mod lossy; + /// A trait to abstract the idea of creating a new instance of a type from a /// string. /// diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index c3162899bbd..149269263dc 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -33,6 +33,7 @@ #![feature(sort_internals)] #![feature(specialization)] #![feature(step_trait)] +#![feature(str_internals)] #![feature(test)] #![feature(trusted_len)] #![feature(try_trait)] @@ -68,4 +69,5 @@ mod ptr; mod result; mod slice; mod str; +mod str_lossy; mod tuple; diff --git a/src/libcore/tests/str_lossy.rs b/src/libcore/tests/str_lossy.rs new file mode 100644 index 00000000000..69e28256da9 --- /dev/null +++ b/src/libcore/tests/str_lossy.rs @@ -0,0 +1,91 @@ +// Copyright 2012-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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::str::lossy::*; + +#[test] +fn chunks() { + let mut iter = Utf8Lossy::from_bytes(b"hello").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "hello", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes("ศไทย中华Việt Nam".as_bytes()).chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "ศไทย中华Việt Nam", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"Hello\xC2 There\xFF Goodbye").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC2", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xFF", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC0", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xE6\x83", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"\xF5foo\xF5\x80bar").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF5", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF5", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"\xF1foo\xF1\x80bar\xF1\x80\x80baz").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF1", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF1\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF1\x80\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"\xF4foo\xF4\x80bar\xF4\xBFbaz").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF4", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF4\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF4", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + let mut iter = Utf8Lossy::from_bytes(b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF0", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "foo\u{10000}bar", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); + + // surrogates + let mut iter = Utf8Lossy::from_bytes(b"\xED\xA0\x80foo\xED\xBF\xBFbar").chunks(); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xED", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xA0", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xED", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); + assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next()); + assert_eq!(None, iter.next()); +} + +#[test] +fn display() { + assert_eq!( + "Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", + &format!("{}", Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye"))); +} + +#[test] +fn debug() { + assert_eq!( + "\"Hello\\xc0\\x80 There\\xe6\\x83 Goodbye\\u{10d4ea}\"", + &format!("{:?}", Utf8Lossy::from_bytes( + b"Hello\xC0\x80 There\xE6\x83 Goodbye\xf4\x8d\x93\xaa"))); +} diff --git a/src/libstd/sys/redox/os_str.rs b/src/libstd/sys/redox/os_str.rs index da27787babb..eb3a1ead58c 100644 --- a/src/libstd/sys/redox/os_str.rs +++ b/src/libstd/sys/redox/os_str.rs @@ -19,7 +19,7 @@ use rc::Rc; use sync::Arc; use sys_common::{AsInner, IntoInner}; use sys_common::bytestring::debug_fmt_bytestring; -use std_unicode::lossy::Utf8Lossy; +use core::str::lossy::Utf8Lossy; #[derive(Clone, Hash)] pub struct Buf { diff --git a/src/libstd/sys/unix/os_str.rs b/src/libstd/sys/unix/os_str.rs index e43bc6da5f1..01c0fb830aa 100644 --- a/src/libstd/sys/unix/os_str.rs +++ b/src/libstd/sys/unix/os_str.rs @@ -19,7 +19,7 @@ use rc::Rc; use sync::Arc; use sys_common::{AsInner, IntoInner}; use sys_common::bytestring::debug_fmt_bytestring; -use std_unicode::lossy::Utf8Lossy; +use core::str::lossy::Utf8Lossy; #[derive(Clone, Hash)] pub struct Buf { diff --git a/src/libstd/sys/wasm/os_str.rs b/src/libstd/sys/wasm/os_str.rs index 84f560af69b..e0da5bdf36c 100644 --- a/src/libstd/sys/wasm/os_str.rs +++ b/src/libstd/sys/wasm/os_str.rs @@ -19,7 +19,7 @@ use rc::Rc; use sync::Arc; use sys_common::{AsInner, IntoInner}; use sys_common::bytestring::debug_fmt_bytestring; -use std_unicode::lossy::Utf8Lossy; +use core::str::lossy::Utf8Lossy; #[derive(Clone, Hash)] pub struct Buf { diff --git a/src/libstd/sys_common/bytestring.rs b/src/libstd/sys_common/bytestring.rs index eb9cad09915..971b83938c1 100644 --- a/src/libstd/sys_common/bytestring.rs +++ b/src/libstd/sys_common/bytestring.rs @@ -11,7 +11,7 @@ #![allow(dead_code)] use fmt::{Formatter, Result, Write}; -use std_unicode::lossy::{Utf8Lossy, Utf8LossyChunk}; +use core::str::lossy::{Utf8Lossy, Utf8LossyChunk}; pub fn debug_fmt_bytestring(slice: &[u8], f: &mut Formatter) -> Result { // Writes out a valid unicode string with the correct escape sequences diff --git a/src/libstd_unicode/Cargo.toml b/src/libstd_unicode/Cargo.toml index 283070a0e2c..b1c55c2e4b6 100644 --- a/src/libstd_unicode/Cargo.toml +++ b/src/libstd_unicode/Cargo.toml @@ -9,10 +9,6 @@ path = "lib.rs" test = false bench = false -[[test]] -name = "std_unicode_tests" -path = "tests/lib.rs" - [dependencies] core = { path = "../libcore" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index cf8c101a2f9..106a2c0f0c5 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -45,7 +45,6 @@ mod tables; mod u_str; mod version; pub mod char; -pub mod lossy; #[allow(deprecated)] pub mod str { diff --git a/src/libstd_unicode/lossy.rs b/src/libstd_unicode/lossy.rs deleted file mode 100644 index cc8e93308a5..00000000000 --- a/src/libstd_unicode/lossy.rs +++ /dev/null @@ -1,213 +0,0 @@ -// Copyright 2012-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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::str as core_str; -use core::fmt; -use core::fmt::Write; -use char; -use core::mem; - - -/// Lossy UTF-8 string. -#[unstable(feature = "str_internals", issue = "0")] -pub struct Utf8Lossy { - bytes: [u8] -} - -impl Utf8Lossy { - pub fn from_str(s: &str) -> &Utf8Lossy { - Utf8Lossy::from_bytes(s.as_bytes()) - } - - pub fn from_bytes(bytes: &[u8]) -> &Utf8Lossy { - unsafe { mem::transmute(bytes) } - } - - pub fn chunks(&self) -> Utf8LossyChunksIter { - Utf8LossyChunksIter { source: &self.bytes } - } -} - - -/// Iterator over lossy UTF-8 string -#[unstable(feature = "str_internals", issue = "0")] -#[allow(missing_debug_implementations)] -pub struct Utf8LossyChunksIter<'a> { - source: &'a [u8], -} - -#[unstable(feature = "str_internals", issue = "0")] -#[derive(PartialEq, Eq, Debug)] -pub struct Utf8LossyChunk<'a> { - /// Sequence of valid chars. - /// Can be empty between broken UTF-8 chars. - pub valid: &'a str, - /// Single broken char, empty if none. - /// Empty iff iterator item is last. - pub broken: &'a [u8], -} - -impl<'a> Iterator for Utf8LossyChunksIter<'a> { - type Item = Utf8LossyChunk<'a>; - - fn next(&mut self) -> Option> { - if self.source.len() == 0 { - return None; - } - - const TAG_CONT_U8: u8 = 128; - fn unsafe_get(xs: &[u8], i: usize) -> u8 { - unsafe { *xs.get_unchecked(i) } - } - fn safe_get(xs: &[u8], i: usize) -> u8 { - if i >= xs.len() { 0 } else { unsafe_get(xs, i) } - } - - let mut i = 0; - while i < self.source.len() { - let i_ = i; - - let byte = unsafe_get(self.source, i); - i += 1; - - if byte < 128 { - - } else { - let w = core_str::utf8_char_width(byte); - - macro_rules! error { () => ({ - unsafe { - let r = Utf8LossyChunk { - valid: core_str::from_utf8_unchecked(&self.source[0..i_]), - broken: &self.source[i_..i], - }; - self.source = &self.source[i..]; - return Some(r); - } - })} - - match w { - 2 => { - if safe_get(self.source, i) & 192 != TAG_CONT_U8 { - error!(); - } - i += 1; - } - 3 => { - match (byte, safe_get(self.source, i)) { - (0xE0, 0xA0 ... 0xBF) => (), - (0xE1 ... 0xEC, 0x80 ... 0xBF) => (), - (0xED, 0x80 ... 0x9F) => (), - (0xEE ... 0xEF, 0x80 ... 0xBF) => (), - _ => { - error!(); - } - } - i += 1; - if safe_get(self.source, i) & 192 != TAG_CONT_U8 { - error!(); - } - i += 1; - } - 4 => { - match (byte, safe_get(self.source, i)) { - (0xF0, 0x90 ... 0xBF) => (), - (0xF1 ... 0xF3, 0x80 ... 0xBF) => (), - (0xF4, 0x80 ... 0x8F) => (), - _ => { - error!(); - } - } - i += 1; - if safe_get(self.source, i) & 192 != TAG_CONT_U8 { - error!(); - } - i += 1; - if safe_get(self.source, i) & 192 != TAG_CONT_U8 { - error!(); - } - i += 1; - } - _ => { - error!(); - } - } - } - } - - let r = Utf8LossyChunk { - valid: unsafe { core_str::from_utf8_unchecked(self.source) }, - broken: &[], - }; - self.source = &[]; - return Some(r); - } -} - - -impl fmt::Display for Utf8Lossy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - // If we're the empty string then our iterator won't actually yield - // anything, so perform the formatting manually - if self.bytes.len() == 0 { - return "".fmt(f) - } - - for Utf8LossyChunk { valid, broken } in self.chunks() { - // If we successfully decoded the whole chunk as a valid string then - // we can return a direct formatting of the string which will also - // respect various formatting flags if possible. - if valid.len() == self.bytes.len() { - assert!(broken.is_empty()); - return valid.fmt(f) - } - - f.write_str(valid)?; - if !broken.is_empty() { - f.write_char(char::REPLACEMENT_CHARACTER)?; - } - } - Ok(()) - } -} - -impl fmt::Debug for Utf8Lossy { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_char('"')?; - - for Utf8LossyChunk { valid, broken } in self.chunks() { - - // Valid part. - // Here we partially parse UTF-8 again which is suboptimal. - { - let mut from = 0; - for (i, c) in valid.char_indices() { - let esc = c.escape_debug(); - // If char needs escaping, flush backlog so far and write, else skip - if esc.len() != 1 { - f.write_str(&valid[from..i])?; - for c in esc { - f.write_char(c)?; - } - from = i + c.len_utf8(); - } - } - f.write_str(&valid[from..])?; - } - - // Broken parts of string as hex escape. - for &b in broken { - write!(f, "\\x{:02x}", b)?; - } - } - - f.write_char('"') - } -} diff --git a/src/libstd_unicode/tests/lib.rs b/src/libstd_unicode/tests/lib.rs deleted file mode 100644 index 9535ec18763..00000000000 --- a/src/libstd_unicode/tests/lib.rs +++ /dev/null @@ -1,15 +0,0 @@ -// Copyright 2012-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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(str_internals, unicode)] - -extern crate std_unicode; - -mod lossy; diff --git a/src/libstd_unicode/tests/lossy.rs b/src/libstd_unicode/tests/lossy.rs deleted file mode 100644 index e05d0668556..00000000000 --- a/src/libstd_unicode/tests/lossy.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2012-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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std_unicode::lossy::*; - -#[test] -fn chunks() { - let mut iter = Utf8Lossy::from_bytes(b"hello").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "hello", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes("ศไทย中华Việt Nam".as_bytes()).chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "ศไทย中华Việt Nam", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"Hello\xC2 There\xFF Goodbye").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC2", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xFF", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "Hello", broken: b"\xC0", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: " There", broken: b"\xE6\x83", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: " Goodbye", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"\xF5foo\xF5\x80bar").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF5", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF5", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"\xF1foo\xF1\x80bar\xF1\x80\x80baz").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF1", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF1\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF1\x80\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"\xF4foo\xF4\x80bar\xF4\xBFbaz").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF4", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xF4\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"\xF4", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "baz", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - let mut iter = Utf8Lossy::from_bytes(b"\xF0\x80\x80\x80foo\xF0\x90\x80\x80bar").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xF0", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "foo\u{10000}bar", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); - - // surrogates - let mut iter = Utf8Lossy::from_bytes(b"\xED\xA0\x80foo\xED\xBF\xBFbar").chunks(); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xED", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xA0", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\x80", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "foo", broken: b"\xED", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "", broken: b"\xBF", }), iter.next()); - assert_eq!(Some(Utf8LossyChunk { valid: "bar", broken: b"", }), iter.next()); - assert_eq!(None, iter.next()); -} - -#[test] -fn display() { - assert_eq!( - "Hello\u{FFFD}\u{FFFD} There\u{FFFD} Goodbye", - &format!("{}", Utf8Lossy::from_bytes(b"Hello\xC0\x80 There\xE6\x83 Goodbye"))); -} - -#[test] -fn debug() { - assert_eq!( - "\"Hello\\xc0\\x80 There\\xe6\\x83 Goodbye\\u{10d4ea}\"", - &format!("{:?}", Utf8Lossy::from_bytes( - b"Hello\xC0\x80 There\xE6\x83 Goodbye\xf4\x8d\x93\xaa"))); -} -- cgit 1.4.1-3-g733a5 From b2027ef17c03e47a4d716d8ea8148ed785934b04 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 5 Apr 2018 17:20:08 +0200 Subject: Deprecate the std_unicode crate --- src/Cargo.lock | 1 - src/ci/docker/wasm32-unknown/Dockerfile | 1 - src/doc/unstable-book/src/language-features/lang-items.md | 2 +- src/liballoc/Cargo.toml | 1 - src/liballoc/lib.rs | 2 -- src/liballoc/str.rs | 7 +++---- src/liballoc/string.rs | 2 +- src/liballoc/tests/lib.rs | 2 +- src/liballoc/tests/str.rs | 2 +- src/liballoc/tests/string.rs | 2 +- src/libcore/char.rs | 2 +- src/librustdoc/lib.rs | 1 - src/libstd/lib.rs | 3 +-- src/libstd_unicode/lib.rs | 1 + src/libsyntax/lib.rs | 2 +- src/libsyntax/parse/lexer/mod.rs | 2 +- 16 files changed, 13 insertions(+), 20 deletions(-) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index a60679e417a..6e7c4b67acf 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -13,7 +13,6 @@ dependencies = [ "compiler_builtins 0.0.0", "core 0.0.0", "rand 0.4.2 (registry+https://github.com/rust-lang/crates.io-index)", - "std_unicode 0.0.0", ] [[package]] diff --git a/src/ci/docker/wasm32-unknown/Dockerfile b/src/ci/docker/wasm32-unknown/Dockerfile index 6c0ec1ad9d4..853923ad947 100644 --- a/src/ci/docker/wasm32-unknown/Dockerfile +++ b/src/ci/docker/wasm32-unknown/Dockerfile @@ -34,4 +34,3 @@ ENV SCRIPT python2.7 /checkout/x.py test --target $TARGETS \ src/test/mir-opt \ src/test/codegen-units \ src/libcore \ - src/libstd_unicode/ \ diff --git a/src/doc/unstable-book/src/language-features/lang-items.md b/src/doc/unstable-book/src/language-features/lang-items.md index c5167418614..6a7aea7f1c2 100644 --- a/src/doc/unstable-book/src/language-features/lang-items.md +++ b/src/doc/unstable-book/src/language-features/lang-items.md @@ -243,7 +243,7 @@ the source code. - `usize`: `libcore/num/mod.rs` - `f32`: `libstd/f32.rs` - `f64`: `libstd/f64.rs` - - `char`: `libstd_unicode/char.rs` + - `char`: `libcore/char.rs` - `slice`: `liballoc/slice.rs` - `str`: `liballoc/str.rs` - `const_ptr`: `libcore/ptr.rs` diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 2eb8ea12604..6383bd1e941 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -9,7 +9,6 @@ path = "lib.rs" [dependencies] core = { path = "../libcore" } -std_unicode = { path = "../libstd_unicode" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } [dev-dependencies] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index b08bd66b47c..d1a91ab4a9c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -135,8 +135,6 @@ extern crate test; #[cfg(test)] extern crate rand; -extern crate std_unicode; - // Module with internal macros used by other modules (needs to be included before other modules). #[macro_use] mod macros; diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index d5ef41df0d8..eaca9eb49f9 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -45,12 +45,11 @@ use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; use core::ptr; use core::iter::FusedIterator; -use std_unicode::str::{UnicodeStr, Utf16Encoder}; +use core::unicode::str::{UnicodeStr, Utf16Encoder}; use vec_deque::VecDeque; use borrow::{Borrow, ToOwned}; use string::String; -use std_unicode; use vec::Vec; use slice::{SliceConcatExt, SliceIndex}; use boxed::Box; @@ -75,7 +74,7 @@ pub use core::str::{from_utf8, from_utf8_mut, Chars, CharIndices, Bytes}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{from_utf8_unchecked, from_utf8_unchecked_mut, ParseBoolError}; #[stable(feature = "rust1", since = "1.0.0")] -pub use std_unicode::str::SplitWhitespace; +pub use core::unicode::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; @@ -1960,7 +1959,7 @@ impl str { } fn case_ignoreable_then_cased>(iter: I) -> bool { - use std_unicode::derived_property::{Cased, Case_Ignorable}; + use core::unicode::derived_property::{Cased, Case_Ignorable}; match iter.skip_while(|&c| Case_Ignorable(c)).next() { Some(c) => Cased(c), None => false, diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 5f90e28cb3c..a902f0bb06b 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -64,7 +64,7 @@ use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds}; use core::ptr; use core::str::pattern::Pattern; use core::str::lossy; -use std_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; +use core::unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; use borrow::{Cow, ToOwned}; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 17f1d0464a5..fddf341d0d1 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -29,7 +29,7 @@ #![feature(inclusive_range_fields)] extern crate alloc_system; -extern crate std_unicode; +extern crate core; extern crate rand; use std::hash::{Hash, Hasher}; diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a14a5d32738..763dbe675b9 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -1204,7 +1204,7 @@ fn test_rev_split_char_iterator_no_trailing() { #[test] fn test_utf16_code_units() { - use std_unicode::str::Utf16Encoder; + use core::unicode::str::Utf16Encoder; assert_eq!(Utf16Encoder::new(vec!['é', '\u{1F4A9}'].into_iter()).collect::>(), [0xE9, 0xD83D, 0xDCA9]) } diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index cb4a17a22d8..33f20be100d 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -132,7 +132,7 @@ fn test_from_utf16() { let s_as_utf16 = s.encode_utf16().collect::>(); let u_as_string = String::from_utf16(&u).unwrap(); - assert!(::std_unicode::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); + assert!(::core::unicode::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); assert_eq!(s_as_utf16, u); assert_eq!(u_as_string, s); diff --git a/src/libcore/char.rs b/src/libcore/char.rs index b5554a59db5..6e2626cc362 100644 --- a/src/libcore/char.rs +++ b/src/libcore/char.rs @@ -10,7 +10,7 @@ //! Character manipulation. //! -//! For more details, see ::std_unicode::char (a.k.a. std::char) +//! For more details, see ::core::unicode::char (a.k.a. std::char) #![allow(non_snake_case)] #![stable(feature = "core_char", since = "1.2.0")] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 730f61e0aa6..9ac034869ac 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -41,7 +41,6 @@ extern crate serialize; #[macro_use] extern crate syntax; extern crate syntax_pos; extern crate test as testing; -extern crate std_unicode; #[macro_use] extern crate log; extern crate rustc_errors as errors; extern crate pulldown_cmark; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 672723341eb..16bca9ddcd3 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -354,7 +354,6 @@ extern crate core as __core; #[macro_reexport(vec, format)] extern crate alloc; extern crate alloc_system; -extern crate std_unicode; #[doc(masked)] extern crate libc; @@ -455,7 +454,7 @@ pub use alloc::string; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc::vec; #[stable(feature = "rust1", since = "1.0.0")] -pub use std_unicode::char; +pub use core::unicode::char; #[stable(feature = "i128", since = "1.26.0")] pub use core::u128; diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index 8cdeb6c8ad1..29de017c64d 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -31,5 +31,6 @@ #![feature(unicode)] #![feature(staged_api)] +#![rustc_deprecated(since = "1.27.0", reason = "moved into libcore")] pub use core::unicode::*; diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index d80430f609b..9de905c01d6 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -32,9 +32,9 @@ extern crate rustc_cratesio_shim; #[macro_use] extern crate bitflags; +extern crate core; extern crate serialize; #[macro_use] extern crate log; -extern crate std_unicode; pub extern crate rustc_errors as errors; extern crate syntax_pos; extern crate rustc_data_structures; diff --git a/src/libsyntax/parse/lexer/mod.rs b/src/libsyntax/parse/lexer/mod.rs index 39b2f77f230..cb3323c7eca 100644 --- a/src/libsyntax/parse/lexer/mod.rs +++ b/src/libsyntax/parse/lexer/mod.rs @@ -15,7 +15,7 @@ use errors::{FatalError, DiagnosticBuilder}; use parse::{token, ParseSess}; use str::char_at; use symbol::{Symbol, keywords}; -use std_unicode::property::Pattern_White_Space; +use core::unicode::property::Pattern_White_Space; use std::borrow::Cow; use std::char; -- cgit 1.4.1-3-g733a5 From 939692409da499ff3d498eae782620435f16a981 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 5 Apr 2018 17:56:46 +0200 Subject: Reexport from core::unicode::char in core::char rather than vice versa --- src/liballoc/string.rs | 2 +- src/liballoc/tests/string.rs | 2 +- src/libcore/char/mod.rs | 12 ++++++++++++ src/libcore/unicode/char.rs | 21 +-------------------- src/libcore/unicode/mod.rs | 6 +++--- src/libstd/lib.rs | 2 +- 6 files changed, 19 insertions(+), 26 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index a902f0bb06b..29d759b1f00 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -56,6 +56,7 @@ #![stable(feature = "rust1", since = "1.0.0")] +use core::char::{decode_utf16, REPLACEMENT_CHARACTER}; use core::fmt; use core::hash; use core::iter::{FromIterator, FusedIterator}; @@ -64,7 +65,6 @@ use core::ops::{self, Add, AddAssign, Index, IndexMut, RangeBounds}; use core::ptr; use core::str::pattern::Pattern; use core::str::lossy; -use core::unicode::char::{decode_utf16, REPLACEMENT_CHARACTER}; use borrow::{Cow, ToOwned}; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 33f20be100d..17d53e4cf3e 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -132,7 +132,7 @@ fn test_from_utf16() { let s_as_utf16 = s.encode_utf16().collect::>(); let u_as_string = String::from_utf16(&u).unwrap(); - assert!(::core::unicode::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); + assert!(::core::char::decode_utf16(u.iter().cloned()).all(|r| r.is_ok())); assert_eq!(s_as_utf16, u); assert_eq!(u_as_string, s); diff --git a/src/libcore/char/mod.rs b/src/libcore/char/mod.rs index c6b620e5238..3efa8396331 100644 --- a/src/libcore/char/mod.rs +++ b/src/libcore/char/mod.rs @@ -15,6 +15,18 @@ #![allow(non_snake_case)] #![stable(feature = "core_char", since = "1.2.0")] +// stable re-exports +#[stable(feature = "rust1", since = "1.0.0")] +pub use unicode::char::{ToLowercase, ToUppercase}; +#[stable(feature = "decode_utf16", since = "1.9.0")] +pub use unicode::char::{decode_utf16, DecodeUtf16, DecodeUtf16Error}; + +// unstable re-exports +#[unstable(feature = "unicode", issue = "27783")] +pub use unicode::tables::{UNICODE_VERSION}; +#[unstable(feature = "unicode", issue = "27783")] +pub use unicode::version::UnicodeVersion; + mod printable; use self::printable::is_printable; diff --git a/src/libcore/unicode/char.rs b/src/libcore/unicode/char.rs index 0e8b09f621a..e75338aedf1 100644 --- a/src/libcore/unicode/char.rs +++ b/src/libcore/unicode/char.rs @@ -28,31 +28,12 @@ #![stable(feature = "rust1", since = "1.0.0")] +use char::*; use char::CharExt as C; use iter::FusedIterator; use fmt::{self, Write}; use unicode::tables::{conversions, derived_property, general_category, property}; -// stable re-exports -#[stable(feature = "rust1", since = "1.0.0")] -pub use char::{MAX, from_digit, from_u32, from_u32_unchecked}; -#[stable(feature = "rust1", since = "1.0.0")] -pub use char::{EscapeDebug, EscapeDefault, EscapeUnicode}; -#[stable(feature = "decode_utf16", since = "1.9.0")] -pub use char::REPLACEMENT_CHARACTER; -#[stable(feature = "char_from_str", since = "1.20.0")] -pub use char::ParseCharError; - -// unstable re-exports -#[stable(feature = "try_from", since = "1.26.0")] -pub use char::CharTryFromError; -#[unstable(feature = "decode_utf8", issue = "33906")] -pub use char::{DecodeUtf8, decode_utf8}; -#[unstable(feature = "unicode", issue = "27783")] -pub use unicode::tables::{UNICODE_VERSION}; -#[unstable(feature = "unicode", issue = "27783")] -pub use unicode::version::UnicodeVersion; - /// Returns an iterator that yields the lowercase equivalent of a `char`. /// /// This `struct` is created by the [`to_lowercase`] method on [`char`]. See diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index aaf8081799f..0ea1aa12146 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -12,11 +12,11 @@ #![allow(missing_docs)] mod bool_trie; -mod tables; -mod version; +pub(crate) mod tables; +pub(crate) mod version; pub mod str; -pub mod char; +pub(crate) mod char; // For use in liballoc, not re-exported in libstd. pub mod derived_property { diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 16bca9ddcd3..94e48732c26 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -454,7 +454,7 @@ pub use alloc::string; #[stable(feature = "rust1", since = "1.0.0")] pub use alloc::vec; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::unicode::char; +pub use core::char; #[stable(feature = "i128", since = "1.26.0")] pub use core::u128; -- cgit 1.4.1-3-g733a5 From 0d9afcd9b9f881545c8b722855f7e39361495d27 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 5 Apr 2018 19:00:48 +0200 Subject: Merge core::unicode::str into core::str And the UnicodeStr trait into StrExt --- src/liballoc/str.rs | 16 ++-- src/liballoc/tests/str.rs | 2 +- src/libcore/str/mod.rs | 116 +++++++++++++++++++++++++++- src/libcore/unicode/mod.rs | 60 ++++++++++++++- src/libcore/unicode/str.rs | 186 --------------------------------------------- 5 files changed, 182 insertions(+), 198 deletions(-) delete mode 100644 src/libcore/unicode/str.rs (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index eaca9eb49f9..0b961c2c186 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -45,7 +45,7 @@ use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; use core::ptr; use core::iter::FusedIterator; -use core::unicode::str::{UnicodeStr, Utf16Encoder}; +use core::unicode::Utf16Encoder; use vec_deque::VecDeque; use borrow::{Borrow, ToOwned}; @@ -74,7 +74,7 @@ pub use core::str::{from_utf8, from_utf8_mut, Chars, CharIndices, Bytes}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{from_utf8_unchecked, from_utf8_unchecked_mut, ParseBoolError}; #[stable(feature = "rust1", since = "1.0.0")] -pub use core::unicode::str::SplitWhitespace; +pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; @@ -800,7 +800,7 @@ impl str { #[stable(feature = "split_whitespace", since = "1.1.0")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace { - UnicodeStr::split_whitespace(self) + StrExt::split_whitespace(self) } /// An iterator over the lines of a string, as string slices. @@ -1570,7 +1570,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim(&self) -> &str { - UnicodeStr::trim(self) + StrExt::trim(self) } /// Returns a string slice with leading whitespace removed. @@ -1606,7 +1606,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left(&self) -> &str { - UnicodeStr::trim_left(self) + StrExt::trim_left(self) } /// Returns a string slice with trailing whitespace removed. @@ -1642,7 +1642,7 @@ impl str { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right(&self) -> &str { - UnicodeStr::trim_right(self) + StrExt::trim_right(self) } /// Returns a string slice with all prefixes and suffixes that match a @@ -2141,7 +2141,7 @@ impl str { #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] #[inline] pub fn is_whitespace(&self) -> bool { - UnicodeStr::is_whitespace(self) + StrExt::is_whitespace(self) } /// Returns true if this `str` is entirely alphanumeric, and false otherwise. @@ -2160,7 +2160,7 @@ impl str { #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] #[inline] pub fn is_alphanumeric(&self) -> bool { - UnicodeStr::is_alphanumeric(self) + StrExt::is_alphanumeric(self) } /// Checks if all characters in this string are within the ASCII range. diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 763dbe675b9..2df8ca63a3e 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -1204,7 +1204,7 @@ fn test_rev_split_char_iterator_no_trailing() { #[test] fn test_utf16_code_units() { - use core::unicode::str::Utf16Encoder; + use core::unicode::Utf16Encoder; assert_eq!(Utf16Encoder::new(vec!['é', '\u{1F4A9}'].into_iter()).collect::>(), [0xE9, 0xD83D, 0xDCA9]) } diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 7a97d89dcf9..f1fe23092de 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -19,7 +19,7 @@ use self::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use char; use fmt; -use iter::{Map, Cloned, FusedIterator, TrustedLen}; +use iter::{Map, Cloned, FusedIterator, TrustedLen, Filter}; use iter_private::TrustedRandomAccess; use slice::{self, SliceIndex}; use mem; @@ -2216,6 +2216,18 @@ pub trait StrExt { fn is_empty(&self) -> bool; #[stable(feature = "core", since = "1.6.0")] fn parse(&self) -> Result; + #[stable(feature = "split_whitespace", since = "1.1.0")] + fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; + #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] + fn is_whitespace(&self) -> bool; + #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] + fn is_alphanumeric(&self) -> bool; + #[stable(feature = "rust1", since = "1.0.0")] + fn trim(&self) -> &str; + #[stable(feature = "rust1", since = "1.0.0")] + fn trim_left(&self) -> &str; + #[stable(feature = "rust1", since = "1.0.0")] + fn trim_right(&self) -> &str; } // truncate `&str` to length at most equal to `max` @@ -2536,6 +2548,36 @@ impl StrExt for str { #[inline] fn parse(&self) -> Result { FromStr::from_str(self) } + + #[inline] + fn split_whitespace(&self) -> SplitWhitespace { + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } + } + + #[inline] + fn is_whitespace(&self) -> bool { + self.chars().all(|c| c.is_whitespace()) + } + + #[inline] + fn is_alphanumeric(&self) -> bool { + self.chars().all(|c| c.is_alphanumeric()) + } + + #[inline] + fn trim(&self) -> &str { + self.trim_matches(|c: char| c.is_whitespace()) + } + + #[inline] + fn trim_left(&self) -> &str { + self.trim_left_matches(|c: char| c.is_whitespace()) + } + + #[inline] + fn trim_right(&self) -> &str { + self.trim_right_matches(|c: char| c.is_whitespace()) + } } #[stable(feature = "rust1", since = "1.0.0")] @@ -2551,3 +2593,75 @@ impl<'a> Default for &'a str { /// Creates an empty str fn default() -> &'a str { "" } } + +/// An iterator over the non-whitespace substrings of a string, +/// separated by any amount of whitespace. +/// +/// This struct is created by the [`split_whitespace`] method on [`str`]. +/// See its documentation for more. +/// +/// [`split_whitespace`]: ../../std/primitive.str.html#method.split_whitespace +/// [`str`]: ../../std/primitive.str.html +#[stable(feature = "split_whitespace", since = "1.1.0")] +#[derive(Clone, Debug)] +pub struct SplitWhitespace<'a> { + inner: Filter, IsNotEmpty>, +} + +#[derive(Clone)] +struct IsWhitespace; + +impl FnOnce<(char, )> for IsWhitespace { + type Output = bool; + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (char, )) -> bool { + self.call_mut(arg) + } +} + +impl FnMut<(char, )> for IsWhitespace { + #[inline] + extern "rust-call" fn call_mut(&mut self, arg: (char, )) -> bool { + arg.0.is_whitespace() + } +} + +#[derive(Clone)] +struct IsNotEmpty; + +impl<'a, 'b> FnOnce<(&'a &'b str, )> for IsNotEmpty { + type Output = bool; + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (&&str, )) -> bool { + self.call_mut(arg) + } +} + +impl<'a, 'b> FnMut<(&'a &'b str, )> for IsNotEmpty { + #[inline] + extern "rust-call" fn call_mut(&mut self, arg: (&&str, )) -> bool { + !arg.0.is_empty() + } +} + + +#[stable(feature = "split_whitespace", since = "1.1.0")] +impl<'a> Iterator for SplitWhitespace<'a> { + type Item = &'a str; + + fn next(&mut self) -> Option<&'a str> { + self.inner.next() + } +} + +#[stable(feature = "split_whitespace", since = "1.1.0")] +impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { + fn next_back(&mut self) -> Option<&'a str> { + self.inner.next_back() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a> FusedIterator for SplitWhitespace<'a> {} diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index 060c55286fe..3413476fd22 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -15,8 +15,6 @@ mod bool_trie; pub(crate) mod tables; pub(crate) mod version; -pub mod str; - // For use in liballoc, not re-exported in libstd. pub mod derived_property { pub use unicode::tables::derived_property::{Case_Ignorable, Cased}; @@ -26,3 +24,61 @@ pub mod derived_property { pub mod property { pub use unicode::tables::property::Pattern_White_Space; } + +use iter::FusedIterator; + +/// Iterator adaptor for encoding `char`s to UTF-16. +#[derive(Clone)] +#[allow(missing_debug_implementations)] +pub struct Utf16Encoder { + chars: I, + extra: u16, +} + +impl Utf16Encoder { + /// Create a UTF-16 encoder from any `char` iterator. + pub fn new(chars: I) -> Utf16Encoder + where I: Iterator + { + Utf16Encoder { + chars, + extra: 0, + } + } +} + +impl Iterator for Utf16Encoder + where I: Iterator +{ + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0; 2]; + self.chars.next().map(|ch| { + let n = ch.encode_utf16(&mut buf).len(); + if n == 2 { + self.extra = buf[1]; + } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.chars.size_hint(); + // every char gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +impl FusedIterator for Utf16Encoder + where I: FusedIterator {} diff --git a/src/libcore/unicode/str.rs b/src/libcore/unicode/str.rs deleted file mode 100644 index 0882984e077..00000000000 --- a/src/libcore/unicode/str.rs +++ /dev/null @@ -1,186 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Unicode-intensive string manipulations. - -use char; -use iter::{Filter, FusedIterator}; -use str::Split; - -/// An iterator over the non-whitespace substrings of a string, -/// separated by any amount of whitespace. -/// -/// This struct is created by the [`split_whitespace`] method on [`str`]. -/// See its documentation for more. -/// -/// [`split_whitespace`]: ../../std/primitive.str.html#method.split_whitespace -/// [`str`]: ../../std/primitive.str.html -#[stable(feature = "split_whitespace", since = "1.1.0")] -#[derive(Clone, Debug)] -pub struct SplitWhitespace<'a> { - inner: Filter, IsNotEmpty>, -} - -/// Methods for Unicode string slices -#[allow(missing_docs)] // docs in liballoc -pub trait UnicodeStr { - fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; - fn is_whitespace(&self) -> bool; - fn is_alphanumeric(&self) -> bool; - fn trim(&self) -> &str; - fn trim_left(&self) -> &str; - fn trim_right(&self) -> &str; -} - -impl UnicodeStr for str { - #[inline] - fn split_whitespace(&self) -> SplitWhitespace { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } - } - - #[inline] - fn is_whitespace(&self) -> bool { - self.chars().all(|c| c.is_whitespace()) - } - - #[inline] - fn is_alphanumeric(&self) -> bool { - self.chars().all(|c| c.is_alphanumeric()) - } - - #[inline] - fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) - } -} - -/// Iterator adaptor for encoding `char`s to UTF-16. -#[derive(Clone)] -#[allow(missing_debug_implementations)] -pub struct Utf16Encoder { - chars: I, - extra: u16, -} - -impl Utf16Encoder { - /// Create a UTF-16 encoder from any `char` iterator. - pub fn new(chars: I) -> Utf16Encoder - where I: Iterator - { - Utf16Encoder { - chars, - extra: 0, - } - } -} - -impl Iterator for Utf16Encoder - where I: Iterator -{ - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; 2]; - self.chars.next().map(|ch| { - let n = ch.encode_utf16(&mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (low, high) = self.chars.size_hint(); - // every char gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low, high.and_then(|n| n.checked_mul(2))) - } -} - -impl FusedIterator for Utf16Encoder - where I: FusedIterator {} - -#[derive(Clone)] -struct IsWhitespace; - -impl FnOnce<(char, )> for IsWhitespace { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (char, )) -> bool { - self.call_mut(arg) - } -} - -impl FnMut<(char, )> for IsWhitespace { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (char, )) -> bool { - arg.0.is_whitespace() - } -} - -#[derive(Clone)] -struct IsNotEmpty; - -impl<'a, 'b> FnOnce<(&'a &'b str, )> for IsNotEmpty { - type Output = bool; - - #[inline] - extern "rust-call" fn call_once(mut self, arg: (&&str, )) -> bool { - self.call_mut(arg) - } -} - -impl<'a, 'b> FnMut<(&'a &'b str, )> for IsNotEmpty { - #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&&str, )) -> bool { - !arg.0.is_empty() - } -} - - -#[stable(feature = "split_whitespace", since = "1.1.0")] -impl<'a> Iterator for SplitWhitespace<'a> { - type Item = &'a str; - - fn next(&mut self) -> Option<&'a str> { - self.inner.next() - } -} - -#[stable(feature = "split_whitespace", since = "1.1.0")] -impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { - fn next_back(&mut self) -> Option<&'a str> { - self.inner.next_back() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a> FusedIterator for SplitWhitespace<'a> {} -- cgit 1.4.1-3-g733a5 From d4ed1e6fa4978141408ef01d0d35c7bd142dd164 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 6 Apr 2018 10:24:01 +0200 Subject: Merge unstable Utf16Encoder into EncodeUtf16 --- src/liballoc/str.rs | 27 +++++++++++++++++---- src/liballoc/tests/str.rs | 3 +-- src/libcore/unicode/mod.rs | 58 ---------------------------------------------- 3 files changed, 23 insertions(+), 65 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 0b961c2c186..65df93bd3bb 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -45,7 +45,6 @@ use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; use core::ptr; use core::iter::FusedIterator; -use core::unicode::Utf16Encoder; use vec_deque::VecDeque; use borrow::{Borrow, ToOwned}; @@ -146,7 +145,8 @@ impl> SliceConcatExt for [S] { #[derive(Clone)] #[stable(feature = "encode_utf16", since = "1.8.0")] pub struct EncodeUtf16<'a> { - encoder: Utf16Encoder>, + chars: Chars<'a>, + extra: u16, } #[stable(feature = "collection_debug", since = "1.17.0")] @@ -162,12 +162,29 @@ impl<'a> Iterator for EncodeUtf16<'a> { #[inline] fn next(&mut self) -> Option { - self.encoder.next() + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0; 2]; + self.chars.next().map(|ch| { + let n = ch.encode_utf16(&mut buf).len(); + if n == 2 { + self.extra = buf[1]; + } + buf[0] + }) } #[inline] fn size_hint(&self) -> (usize, Option) { - self.encoder.size_hint() + let (low, high) = self.chars.size_hint(); + // every char gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) } } @@ -870,7 +887,7 @@ impl str { /// ``` #[stable(feature = "encode_utf16", since = "1.8.0")] pub fn encode_utf16(&self) -> EncodeUtf16 { - EncodeUtf16 { encoder: Utf16Encoder::new(self[..].chars()) } + EncodeUtf16 { chars: self[..].chars(), extra: 0 } } /// Returns `true` if the given pattern matches a sub-slice of diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 2df8ca63a3e..a3f4c385fe2 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -1204,8 +1204,7 @@ fn test_rev_split_char_iterator_no_trailing() { #[test] fn test_utf16_code_units() { - use core::unicode::Utf16Encoder; - assert_eq!(Utf16Encoder::new(vec!['é', '\u{1F4A9}'].into_iter()).collect::>(), + assert_eq!("é\u{1F4A9}".encode_utf16().collect::>(), [0xE9, 0xD83D, 0xDCA9]) } diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index 3413476fd22..9ab8cb748b1 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -24,61 +24,3 @@ pub mod derived_property { pub mod property { pub use unicode::tables::property::Pattern_White_Space; } - -use iter::FusedIterator; - -/// Iterator adaptor for encoding `char`s to UTF-16. -#[derive(Clone)] -#[allow(missing_debug_implementations)] -pub struct Utf16Encoder { - chars: I, - extra: u16, -} - -impl Utf16Encoder { - /// Create a UTF-16 encoder from any `char` iterator. - pub fn new(chars: I) -> Utf16Encoder - where I: Iterator - { - Utf16Encoder { - chars, - extra: 0, - } - } -} - -impl Iterator for Utf16Encoder - where I: Iterator -{ - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; 2]; - self.chars.next().map(|ch| { - let n = ch.encode_utf16(&mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (low, high) = self.chars.size_hint(); - // every char gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low, high.and_then(|n| n.checked_mul(2))) - } -} - -impl FusedIterator for Utf16Encoder - where I: FusedIterator {} -- cgit 1.4.1-3-g733a5 From ef41788cf37074e44f70257508c97efd539a7f29 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 6 Apr 2018 14:23:00 +0200 Subject: Mark the rest of the `unicode` feature flag as perma-unstable. --- src/liballoc/lib.rs | 2 +- src/liballoc/tests/lib.rs | 1 - src/libcore/unicode/mod.rs | 2 +- src/librustdoc/lib.rs | 1 - src/libstd/lib.rs | 1 - src/libstd_unicode/lib.rs | 2 +- src/libsyntax/lib.rs | 2 +- 7 files changed, 4 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index d1a91ab4a9c..69fc007ab7c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -113,7 +113,7 @@ #![feature(trusted_len)] #![feature(try_reserve)] #![feature(unboxed_closures)] -#![feature(unicode)] +#![feature(unicode_internals)] #![feature(unsize)] #![feature(allocator_internals)] #![feature(on_unimplemented)] diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index fddf341d0d1..32272169307 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -24,7 +24,6 @@ #![feature(string_retain)] #![feature(try_reserve)] #![feature(unboxed_closures)] -#![feature(unicode)] #![feature(exact_chunks)] #![feature(inclusive_range_fields)] diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index 0fbc4dcd175..b6b033adc04 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![unstable(feature = "unicode", issue = "27783")] +#![unstable(feature = "unicode_internals", issue = "0")] #![allow(missing_docs)] mod bool_trie; diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index 9ac034869ac..4a062e9a55b 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -20,7 +20,6 @@ #![feature(fs_read_write)] #![feature(set_stdio)] #![feature(test)] -#![feature(unicode)] #![feature(vec_remove_item)] #![feature(entry_and_modify)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 94e48732c26..c82d600e4a1 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -307,7 +307,6 @@ #![feature(toowned_clone_into)] #![feature(try_reserve)] #![feature(unboxed_closures)] -#![feature(unicode)] #![feature(untagged_unions)] #![feature(unwind_attributes)] #![feature(vec_push_all)] diff --git a/src/libstd_unicode/lib.rs b/src/libstd_unicode/lib.rs index 29de017c64d..c0d47f1fcb4 100644 --- a/src/libstd_unicode/lib.rs +++ b/src/libstd_unicode/lib.rs @@ -29,7 +29,7 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] -#![feature(unicode)] +#![feature(unicode_internals)] #![feature(staged_api)] #![rustc_deprecated(since = "1.27.0", reason = "moved into libcore")] diff --git a/src/libsyntax/lib.rs b/src/libsyntax/lib.rs index 9de905c01d6..4e1e9fa2b46 100644 --- a/src/libsyntax/lib.rs +++ b/src/libsyntax/lib.rs @@ -19,7 +19,7 @@ html_root_url = "https://doc.rust-lang.org/nightly/", test(attr(deny(warnings))))] -#![feature(unicode)] +#![feature(unicode_internals)] #![feature(rustc_diagnostic_macros)] #![feature(non_exhaustive)] #![feature(const_atomic_usize_new)] -- cgit 1.4.1-3-g733a5 From 1569f8f812c97002fa54dd6a5778109766a320e1 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Mon, 2 Apr 2018 10:38:07 +0200 Subject: Inline docs for the heap module’s reexports MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/liballoc/heap.rs | 2 ++ src/libstd/heap.rs | 2 +- 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 9296a113071..000c0123d9f 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -19,7 +19,9 @@ use core::intrinsics::{min_align_of_val, size_of_val}; use core::mem::{self, ManuallyDrop}; use core::usize; +#[doc(inline)] pub use core::heap::*; + #[doc(hidden)] pub mod __core { pub use core::*; diff --git a/src/libstd/heap.rs b/src/libstd/heap.rs index 4a391372c3a..2cf06018087 100644 --- a/src/libstd/heap.rs +++ b/src/libstd/heap.rs @@ -14,7 +14,7 @@ pub use alloc::heap::Heap; pub use alloc_system::System; -pub use core::heap::*; +#[doc(inline)] pub use core::heap::*; #[cfg(not(test))] #[doc(hidden)] -- cgit 1.4.1-3-g733a5 From 09e8db1e4f33ec82316e1eeaaedad94fe6e1acb5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 14:41:15 +0200 Subject: Rename `heap` modules in the core, alloc, and std crates to `alloc` --- src/liballoc/alloc.rs | 291 +++++++++++++ src/liballoc/heap.rs | 291 ------------- src/liballoc/lib.rs | 8 +- src/libcore/alloc.rs | 1125 +++++++++++++++++++++++++++++++++++++++++++++++++ src/libcore/heap.rs | 1125 ------------------------------------------------- src/libcore/lib.rs | 6 +- src/libstd/alloc.rs | 176 ++++++++ src/libstd/heap.rs | 176 -------- src/libstd/lib.rs | 6 +- 9 files changed, 1608 insertions(+), 1596 deletions(-) create mode 100644 src/liballoc/alloc.rs delete mode 100644 src/liballoc/heap.rs create mode 100644 src/libcore/alloc.rs delete mode 100644 src/libcore/heap.rs create mode 100644 src/libstd/alloc.rs delete mode 100644 src/libstd/heap.rs (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs new file mode 100644 index 00000000000..000c0123d9f --- /dev/null +++ b/src/liballoc/alloc.rs @@ -0,0 +1,291 @@ +// Copyright 2014-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked \ + slightly, especially to possibly take into account the \ + types being stored to make room for a future \ + tracing garbage collector", + issue = "32838")] + +use core::intrinsics::{min_align_of_val, size_of_val}; +use core::mem::{self, ManuallyDrop}; +use core::usize; + +#[doc(inline)] +pub use core::heap::*; + +#[doc(hidden)] +pub mod __core { + pub use 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, + new_size: usize, + 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, + new_size: usize, + new_align: usize) -> u8; +} + +#[derive(Copy, Clone, Default, Debug)] +pub struct Heap; + +unsafe impl Alloc for Heap { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(ptr) + } + } + + #[inline] + #[cold] + fn oom(&mut self, err: AllocErr) -> ! { + unsafe { + __rust_oom(&err as *const AllocErr as *const u8) + } + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) + } + + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + let mut min = 0; + let mut max = 0; + unsafe { + __rust_usable_size(layout as *const Layout as *const u8, + &mut min, + &mut max); + } + (min, max) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) + -> Result<*mut u8, AllocErr> + { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_realloc(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + mem::forget(err); + Ok(ptr) + } + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let ptr = __rust_alloc_zeroed(layout.size(), + layout.align(), + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(ptr) + } + } + + #[inline] + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_alloc_excess(layout.size(), + layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut size = 0; + let ptr = __rust_realloc_excess(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align(), + &mut size, + &mut *err as *mut AllocErr as *mut u8); + if ptr.is_null() { + Err(ManuallyDrop::into_inner(err)) + } else { + Ok(Excess(ptr, size)) + } + } + + #[inline] + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) + -> Result<(), CannotReallocInPlace> + { + debug_assert!(new_layout.size() >= layout.size()); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_grow_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) + } + } + + #[inline] + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + debug_assert!(new_layout.size() <= layout.size()); + debug_assert!(new_layout.align() == layout.align()); + let ret = __rust_shrink_in_place(ptr, + layout.size(), + layout.align(), + new_layout.size(), + new_layout.align()); + if ret != 0 { + Ok(()) + } else { + Err(CannotReallocInPlace) + } + } +} + +/// The allocator for unique pointers. +// This function must not unwind. If it does, MIR trans will fail. +#[cfg(not(test))] +#[lang = "exchange_malloc"] +#[inline] +unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { + if size == 0 { + align as *mut u8 + } else { + let layout = Layout::from_size_align_unchecked(size, align); + Heap.alloc(layout).unwrap_or_else(|err| { + Heap.oom(err) + }) + } +} + +#[cfg_attr(not(test), lang = "box_free")] +#[inline] +pub(crate) unsafe fn box_free(ptr: *mut T) { + let size = size_of_val(&*ptr); + let align = min_align_of_val(&*ptr); + // We do not allocate for Box when T is ZST, so deallocation is also not necessary. + if size != 0 { + let layout = Layout::from_size_align_unchecked(size, align); + Heap.dealloc(ptr as *mut u8, layout); + } +} + +#[cfg(test)] +mod tests { + extern crate test; + use self::test::Bencher; + use boxed::Box; + use heap::{Heap, Alloc, Layout}; + + #[test] + fn allocate_zeroed() { + unsafe { + let layout = Layout::from_size_align(1024, 1).unwrap(); + let ptr = Heap.alloc_zeroed(layout.clone()) + .unwrap_or_else(|e| Heap.oom(e)); + + let end = ptr.offset(layout.size() as isize); + let mut i = ptr; + while i < end { + assert_eq!(*i, 0); + i = i.offset(1); + } + Heap.dealloc(ptr, layout); + } + } + + #[bench] + fn alloc_owned_small(b: &mut Bencher) { + b.iter(|| { + let _: Box<_> = box 10; + }) + } +} diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs deleted file mode 100644 index 000c0123d9f..00000000000 --- a/src/liballoc/heap.rs +++ /dev/null @@ -1,291 +0,0 @@ -// Copyright 2014-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] - -use core::intrinsics::{min_align_of_val, size_of_val}; -use core::mem::{self, ManuallyDrop}; -use core::usize; - -#[doc(inline)] -pub use core::heap::*; - -#[doc(hidden)] -pub mod __core { - pub use 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, - new_size: usize, - 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, - new_size: usize, - new_align: usize) -> u8; -} - -#[derive(Copy, Clone, Default, Debug)] -pub struct Heap; - -unsafe impl Alloc for Heap { - #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); - let ptr = __rust_alloc(layout.size(), - layout.align(), - &mut *err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) - } else { - Ok(ptr) - } - } - - #[inline] - #[cold] - fn oom(&mut self, err: AllocErr) -> ! { - unsafe { - __rust_oom(&err as *const AllocErr as *const u8) - } - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) - } - - #[inline] - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - let mut min = 0; - let mut max = 0; - unsafe { - __rust_usable_size(layout as *const Layout as *const u8, - &mut min, - &mut max); - } - (min, max) - } - - #[inline] - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) - -> Result<*mut u8, AllocErr> - { - let mut err = ManuallyDrop::new(mem::uninitialized::()); - let ptr = __rust_realloc(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut *err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) - } else { - mem::forget(err); - Ok(ptr) - } - } - - #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); - let ptr = __rust_alloc_zeroed(layout.size(), - layout.align(), - &mut *err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) - } else { - Ok(ptr) - } - } - - #[inline] - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); - let mut size = 0; - let ptr = __rust_alloc_excess(layout.size(), - layout.align(), - &mut size, - &mut *err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) - } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); - let mut size = 0; - let ptr = __rust_realloc_excess(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut size, - &mut *err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) - } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) - -> Result<(), CannotReallocInPlace> - { - debug_assert!(new_layout.size() >= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_grow_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) - } - } - - #[inline] - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - debug_assert!(new_layout.size() <= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_shrink_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) - } - } -} - -/// The allocator for unique pointers. -// This function must not unwind. If it does, MIR trans will fail. -#[cfg(not(test))] -#[lang = "exchange_malloc"] -#[inline] -unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { - if size == 0 { - align as *mut u8 - } else { - let layout = Layout::from_size_align_unchecked(size, align); - Heap.alloc(layout).unwrap_or_else(|err| { - Heap.oom(err) - }) - } -} - -#[cfg_attr(not(test), lang = "box_free")] -#[inline] -pub(crate) unsafe fn box_free(ptr: *mut T) { - let size = size_of_val(&*ptr); - let align = min_align_of_val(&*ptr); - // We do not allocate for Box when T is ZST, so deallocation is also not necessary. - if size != 0 { - let layout = Layout::from_size_align_unchecked(size, align); - Heap.dealloc(ptr as *mut u8, layout); - } -} - -#[cfg(test)] -mod tests { - extern crate test; - use self::test::Bencher; - use boxed::Box; - use heap::{Heap, Alloc, Layout}; - - #[test] - fn allocate_zeroed() { - unsafe { - let layout = Layout::from_size_align(1024, 1).unwrap(); - let ptr = Heap.alloc_zeroed(layout.clone()) - .unwrap_or_else(|e| Heap.oom(e)); - - let end = ptr.offset(layout.size() as isize); - let mut i = ptr; - while i < end { - assert_eq!(*i, 0); - i = i.offset(1); - } - Heap.dealloc(ptr, layout); - } - } - - #[bench] - fn alloc_owned_small(b: &mut Bencher) { - b.iter(|| { - let _: Box<_> = box 10; - }) - } -} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 5ca39442342..617bc5c52b3 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -57,7 +57,7 @@ //! //! ## Heap interfaces //! -//! The [`heap`](heap/index.html) module defines the low-level interface to the +//! The [`alloc`](alloc/index.html) module defines the low-level interface to the //! default global allocator. It is not compatible with the libc allocator API. #![allow(unused_attributes)] @@ -145,7 +145,11 @@ pub use core::heap as allocator; // Heaps provided for low-level allocation strategies -pub mod heap; +pub mod alloc; + +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] +pub use alloc as heap; // Primitive types using the heaps above diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs new file mode 100644 index 00000000000..5c51bb2b51b --- /dev/null +++ b/src/libcore/alloc.rs @@ -0,0 +1,1125 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked \ + slightly, especially to possibly take into account the \ + types being stored to make room for a future \ + tracing garbage collector", + issue = "32838")] + +use cmp; +use fmt; +use mem; +use usize; +use ptr::{self, NonNull}; + +extern { + /// An opaque, unsized type. Used for pointers to allocated memory. + /// + /// This type can only be used behind a pointer like `*mut Void` or `ptr::NonNull`. + /// Such pointers are similar to C’s `void*` type. + pub type Void; +} + +impl Void { + /// Similar to `std::ptr::null`, which requires `T: Sized`. + pub fn null() -> *const Self { + 0 as _ + } + + /// Similar to `std::ptr::null_mut`, which requires `T: Sized`. + pub fn null_mut() -> *mut Self { + 0 as _ + } +} + +/// Represents the combination of a starting address and +/// a total capacity of the returned block. +#[derive(Debug)] +pub struct Excess(pub *mut u8, pub usize); + +fn size_align() -> (usize, usize) { + (mem::size_of::(), mem::align_of::()) +} + +/// Layout of a block of memory. +/// +/// An instance of `Layout` describes a particular layout of memory. +/// You build a `Layout` up as an input to give to an allocator. +/// +/// All layouts have an associated non-negative size and a +/// power-of-two alignment. +/// +/// (Note however that layouts are *not* required to have positive +/// size, even though many allocators require that all memory +/// requests have positive size. A caller to the `Alloc::alloc` +/// method must either ensure that conditions like this are met, or +/// use specific allocators with looser requirements.) +#[derive(Clone, Debug, PartialEq, Eq)] +pub struct Layout { + // size of the requested block of memory, measured in bytes. + size: usize, + + // alignment of the requested block of memory, measured in bytes. + // we ensure that this is always a power-of-two, because API's + // like `posix_memalign` require it and it is a reasonable + // constraint to impose on Layout constructors. + // + // (However, we do not analogously require `align >= sizeof(void*)`, + // even though that is *also* a requirement of `posix_memalign`.) + align: usize, +} + + +// FIXME: audit default implementations for overflow errors, +// (potentially switching to overflowing_add and +// overflowing_mul as necessary). + +impl Layout { + /// Constructs a `Layout` from a given `size` and `align`, + /// or returns `None` if either of the following conditions + /// are not met: + /// + /// * `align` must be a power of two, + /// + /// * `size`, when rounded up to the nearest multiple of `align`, + /// must not overflow (i.e. the rounded value must be less than + /// `usize::MAX`). + #[inline] + pub fn from_size_align(size: usize, align: usize) -> Option { + if !align.is_power_of_two() { + return None; + } + + // (power-of-two implies align != 0.) + + // Rounded up size is: + // size_rounded_up = (size + align - 1) & !(align - 1); + // + // We know from above that align != 0. If adding (align - 1) + // does not overflow, then rounding up will be fine. + // + // Conversely, &-masking with !(align - 1) will subtract off + // only low-order-bits. Thus if overflow occurs with the sum, + // the &-mask cannot subtract enough to undo that overflow. + // + // Above implies that checking for summation overflow is both + // necessary and sufficient. + if size > usize::MAX - (align - 1) { + return None; + } + + unsafe { + Some(Layout::from_size_align_unchecked(size, align)) + } + } + + /// Creates a layout, bypassing all checks. + /// + /// # Safety + /// + /// This function is unsafe as it does not verify that `align` is + /// a power-of-two nor `size` aligned to `align` fits within the + /// address space (i.e. the `Layout::from_size_align` preconditions). + #[inline] + pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { + Layout { size: size, align: align } + } + + /// The minimum size in bytes for a memory block of this layout. + #[inline] + pub fn size(&self) -> usize { self.size } + + /// The minimum byte alignment for a memory block of this layout. + #[inline] + pub fn align(&self) -> usize { self.align } + + /// Constructs a `Layout` suitable for holding a value of type `T`. + pub fn new() -> Self { + let (size, align) = size_align::(); + Layout::from_size_align(size, align).unwrap() + } + + /// Produces layout describing a record that could be used to + /// allocate backing structure for `T` (which could be a trait + /// or other unsized type like a slice). + pub fn for_value(t: &T) -> Self { + let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); + Layout::from_size_align(size, align).unwrap() + } + + /// Creates a layout describing the record that can hold a value + /// of the same layout as `self`, but that also is aligned to + /// alignment `align` (measured in bytes). + /// + /// If `self` already meets the prescribed alignment, then returns + /// `self`. + /// + /// Note that this method does not add any padding to the overall + /// size, regardless of whether the returned layout has a different + /// alignment. In other words, if `K` has size 16, `K.align_to(32)` + /// will *still* have size 16. + /// + /// # Panics + /// + /// Panics if the combination of `self.size` and the given `align` + /// violates the conditions listed in `from_size_align`. + #[inline] + pub fn align_to(&self, align: usize) -> Self { + Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap() + } + + /// Returns the amount of padding we must insert after `self` + /// to ensure that the following address will satisfy `align` + /// (measured in bytes). + /// + /// E.g. if `self.size` is 9, then `self.padding_needed_for(4)` + /// returns 3, because that is the minimum number of bytes of + /// padding required to get a 4-aligned address (assuming that the + /// corresponding memory block starts at a 4-aligned address). + /// + /// The return value of this function has no meaning if `align` is + /// not a power-of-two. + /// + /// Note that the utility of the returned value requires `align` + /// to be less than or equal to the alignment of the starting + /// address for the whole allocated block of memory. One way to + /// satisfy this constraint is to ensure `align <= self.align`. + #[inline] + pub fn padding_needed_for(&self, align: usize) -> usize { + let len = self.size(); + + // Rounded up value is: + // len_rounded_up = (len + align - 1) & !(align - 1); + // and then we return the padding difference: `len_rounded_up - len`. + // + // We use modular arithmetic throughout: + // + // 1. align is guaranteed to be > 0, so align - 1 is always + // valid. + // + // 2. `len + align - 1` can overflow by at most `align - 1`, + // so the &-mask wth `!(align - 1)` will ensure that in the + // case of overflow, `len_rounded_up` will itself be 0. + // Thus the returned padding, when added to `len`, yields 0, + // which trivially satisfies the alignment `align`. + // + // (Of course, attempts to allocate blocks of memory whose + // size and padding overflow in the above manner should cause + // the allocator to yield an error anyway.) + + let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); + return len_rounded_up.wrapping_sub(len); + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with a suitable amount of padding between each to + /// ensure that each instance is given its requested size and + /// alignment. On success, returns `(k, offs)` where `k` is the + /// layout of the array and `offs` is the distance between the start + /// of each element in the array. + /// + /// On arithmetic overflow, returns `None`. + #[inline] + pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { + let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?; + let alloc_size = padded_size.checked_mul(n)?; + + // We can assume that `self.align` is a power-of-two. + // Furthermore, `alloc_size` has already been rounded up + // to a multiple of `self.align`; therefore, the call to + // `Layout::from_size_align` below should never panic. + Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) + } + + /// Creates a layout describing the record for `self` followed by + /// `next`, including any necessary padding to ensure that `next` + /// will be properly aligned. Note that the result layout will + /// satisfy the alignment properties of both `self` and `next`. + /// + /// Returns `Some((k, offset))`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// On arithmetic overflow, returns `None`. + pub fn extend(&self, next: Self) -> Option<(Self, usize)> { + let new_align = cmp::max(self.align, next.align); + let realigned = Layout::from_size_align(self.size, new_align)?; + + let pad = realigned.padding_needed_for(next.align); + + let offset = self.size.checked_add(pad)?; + let new_size = offset.checked_add(next.size)?; + + let layout = Layout::from_size_align(new_size, new_align)?; + Some((layout, offset)) + } + + /// Creates a layout describing the record for `n` instances of + /// `self`, with no padding between each instance. + /// + /// Note that, unlike `repeat`, `repeat_packed` does not guarantee + /// that the repeated instances of `self` will be properly + /// aligned, even if a given instance of `self` is properly + /// aligned. In other words, if the layout returned by + /// `repeat_packed` is used to allocate an array, it is not + /// guaranteed that all elements in the array will be properly + /// aligned. + /// + /// On arithmetic overflow, returns `None`. + pub fn repeat_packed(&self, n: usize) -> Option { + let size = self.size().checked_mul(n)?; + Layout::from_size_align(size, self.align) + } + + /// Creates a layout describing the record for `self` followed by + /// `next` with no additional padding between the two. Since no + /// padding is inserted, the alignment of `next` is irrelevant, + /// and is not incorporated *at all* into the resulting layout. + /// + /// Returns `(k, offset)`, where `k` is layout of the concatenated + /// record and `offset` is the relative location, in bytes, of the + /// start of the `next` embedded within the concatenated record + /// (assuming that the record itself starts at offset 0). + /// + /// (The `offset` is always the same as `self.size()`; we use this + /// signature out of convenience in matching the signature of + /// `extend`.) + /// + /// On arithmetic overflow, returns `None`. + pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { + let new_size = self.size().checked_add(next.size())?; + let layout = Layout::from_size_align(new_size, self.align)?; + Some((layout, self.size())) + } + + /// Creates a layout describing the record for a `[T; n]`. + /// + /// On arithmetic overflow, returns `None`. + pub fn array(n: usize) -> Option { + Layout::new::() + .repeat(n) + .map(|(k, offs)| { + debug_assert!(offs == mem::size_of::()); + k + }) + } +} + +/// The `AllocErr` error specifies whether an allocation failure is +/// specifically due to resource exhaustion or if it is due to +/// something wrong when combining the given input arguments with this +/// allocator. +#[derive(Clone, PartialEq, Eq, Debug)] +pub enum AllocErr { + /// Error due to hitting some resource limit or otherwise running + /// out of memory. This condition strongly implies that *some* + /// series of deallocations would allow a subsequent reissuing of + /// the original allocation request to succeed. + Exhausted { request: Layout }, + + /// Error due to allocator being fundamentally incapable of + /// satisfying the original request. This condition implies that + /// such an allocation request will never succeed on the given + /// allocator, regardless of environment, memory pressure, or + /// other contextual conditions. + /// + /// For example, an allocator that does not support requests for + /// large memory blocks might return this error variant. + Unsupported { details: &'static str }, +} + +impl AllocErr { + #[inline] + pub fn invalid_input(details: &'static str) -> Self { + AllocErr::Unsupported { details: details } + } + #[inline] + pub fn is_memory_exhausted(&self) -> bool { + if let AllocErr::Exhausted { .. } = *self { true } else { false } + } + #[inline] + pub fn is_request_unsupported(&self) -> bool { + if let AllocErr::Unsupported { .. } = *self { true } else { false } + } + #[inline] + pub fn description(&self) -> &str { + match *self { + AllocErr::Exhausted { .. } => "allocator memory exhausted", + AllocErr::Unsupported { .. } => "unsupported allocator request", + } + } +} + +// (we need this for downstream impl of trait Error) +impl fmt::Display for AllocErr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +/// The `CannotReallocInPlace` error is used when `grow_in_place` or +/// `shrink_in_place` were unable to reuse the given memory block for +/// a requested layout. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct CannotReallocInPlace; + +impl CannotReallocInPlace { + pub fn description(&self) -> &str { + "cannot reallocate allocator's memory in place" + } +} + +// (we need this for downstream impl of trait Error) +impl fmt::Display for CannotReallocInPlace { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "{}", self.description()) + } +} + +/// Augments `AllocErr` with a CapacityOverflow variant. +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +pub enum CollectionAllocErr { + /// Error due to the computed capacity exceeding the collection's maximum + /// (usually `isize::MAX` bytes). + CapacityOverflow, + /// Error due to the allocator (see the `AllocErr` type's docs). + AllocErr(AllocErr), +} + +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From for CollectionAllocErr { + fn from(err: AllocErr) -> Self { + CollectionAllocErr::AllocErr(err) + } +} + +// FIXME: docs +pub unsafe trait GlobalAlloc { + unsafe fn alloc(&self, layout: Layout) -> *mut Void; + + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout); + + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + let size = layout.size(); + let ptr = self.alloc(layout); + if !ptr.is_null() { + ptr::write_bytes(ptr as *mut u8, 0, size); + } + ptr + } + + unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { + let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); + let new_ptr = self.alloc(new_layout); + if !new_ptr.is_null() { + ptr::copy_nonoverlapping( + ptr as *const u8, + new_ptr as *mut u8, + cmp::min(old_layout.size(), new_size), + ); + self.dealloc(ptr, old_layout); + } + new_ptr + } +} + +/// An implementation of `Alloc` can allocate, reallocate, and +/// deallocate arbitrary blocks of data described via `Layout`. +/// +/// Some of the methods require that a memory block be *currently +/// allocated* via an allocator. This means that: +/// +/// * the starting address for that memory block was previously +/// returned by a previous call to an allocation method (`alloc`, +/// `alloc_zeroed`, `alloc_excess`, `alloc_one`, `alloc_array`) or +/// reallocation method (`realloc`, `realloc_excess`, or +/// `realloc_array`), and +/// +/// * the memory block has not been subsequently deallocated, where +/// blocks are deallocated either by being passed to a deallocation +/// method (`dealloc`, `dealloc_one`, `dealloc_array`) or by being +/// passed to a reallocation method (see above) that returns `Ok`. +/// +/// A note regarding zero-sized types and zero-sized layouts: many +/// methods in the `Alloc` trait state that allocation requests +/// must be non-zero size, or else undefined behavior can result. +/// +/// * However, some higher-level allocation methods (`alloc_one`, +/// `alloc_array`) are well-defined on zero-sized types and can +/// optionally support them: it is left up to the implementor +/// whether to return `Err`, or to return `Ok` with some pointer. +/// +/// * If an `Alloc` implementation chooses to return `Ok` in this +/// case (i.e. the pointer denotes a zero-sized inaccessible block) +/// then that returned pointer must be considered "currently +/// allocated". On such an allocator, *all* methods that take +/// currently-allocated pointers as inputs must accept these +/// zero-sized pointers, *without* causing undefined behavior. +/// +/// * In other words, if a zero-sized pointer can flow out of an +/// allocator, then that allocator must likewise accept that pointer +/// flowing back into its deallocation and reallocation methods. +/// +/// Some of the methods require that a layout *fit* a memory block. +/// What it means for a layout to "fit" a memory block means (or +/// equivalently, for a memory block to "fit" a layout) is that the +/// following two conditions must hold: +/// +/// 1. The block's starting address must be aligned to `layout.align()`. +/// +/// 2. The block's size must fall in the range `[use_min, use_max]`, where: +/// +/// * `use_min` is `self.usable_size(layout).0`, and +/// +/// * `use_max` is the capacity that was (or would have been) +/// returned when (if) the block was allocated via a call to +/// `alloc_excess` or `realloc_excess`. +/// +/// Note that: +/// +/// * the size of the layout most recently used to allocate the block +/// is guaranteed to be in the range `[use_min, use_max]`, and +/// +/// * a lower-bound on `use_max` can be safely approximated by a call to +/// `usable_size`. +/// +/// * 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 + // behavior in response to a zero size allocation request ; + // e.g. in C, `malloc` of 0 will either return a null pointer or a + // unique pointer, but will not have arbitrary undefined + // behavior. Rust should consider revising the alloc::heap crate + // to reflect this reality.) + + /// Returns a pointer meeting the size and alignment guarantees of + /// `layout`. + /// + /// If this method returns an `Ok(addr)`, then the `addr` returned + /// will be non-null address pointing to a block of storage + /// suitable for holding an instance of `layout`. + /// + /// The returned block of storage may or may not have its contents + /// initialized. (Extension subtraits might restrict this + /// behavior, e.g. to ensure initialization to particular sets of + /// bit patterns.) + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints. + /// + /// Implementations are encouraged to return `Err` on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + + /// Deallocate the memory referenced by `ptr`. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must denote a block of memory currently allocated via + /// this allocator, + /// + /// * `layout` must *fit* that block of memory, + /// + /// * In addition to fitting the block of memory `layout`, the + /// alignment of the `layout` must match the alignment used + /// to allocate that block of memory. + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + + /// Allocator-specific method for signaling an out-of-memory + /// condition. + /// + /// `oom` aborts the thread or process, optionally performing + /// cleanup or logging diagnostic information before panicking or + /// aborting. + /// + /// `oom` is meant to be used by clients unable to cope with an + /// unsatisfied allocation request (signaled by an error such as + /// `AllocErr::Exhausted`), and wish to abandon computation rather + /// than attempt to recover locally. Such clients should pass the + /// signaling error value back into `oom`, where the allocator + /// may incorporate that error value into its diagnostic report + /// before aborting. + /// + /// Implementations of the `oom` method are discouraged from + /// infinitely regressing in nested calls to `oom`. In + /// practice this means implementors should eschew allocating, + /// especially from `self` (directly or indirectly). + /// + /// Implementations of the allocation and reallocation methods + /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from + /// panicking (or aborting) in the event of memory exhaustion; + /// instead they should return an appropriate error from the + /// invoked method, and let the client decide whether to invoke + /// this `oom` method in response. + fn oom(&mut self, _: AllocErr) -> ! { + unsafe { ::intrinsics::abort() } + } + + // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == + // usable_size + + /// Returns bounds on the guaranteed usable size of a successful + /// allocation created with the specified `layout`. + /// + /// In particular, if one has a memory block allocated via a given + /// allocator `a` and layout `k` where `a.usable_size(k)` returns + /// `(l, u)`, then one can pass that block to `a.dealloc()` with a + /// layout in the size range [l, u]. + /// + /// (All implementors of `usable_size` must ensure that + /// `l <= k.size() <= u`) + /// + /// Both the lower- and upper-bounds (`l` and `u` respectively) + /// are provided, because an allocator based on size classes could + /// misbehave if one attempts to deallocate a block without + /// providing a correct value for its size (i.e., one within the + /// range `[l, u]`). + /// + /// Clients who wish to make use of excess capacity are encouraged + /// to use the `alloc_excess` and `realloc_excess` instead, as + /// this method is constrained to report conservative values that + /// serve as valid bounds for *all possible* allocation method + /// calls. + /// + /// However, for clients that do not wish to track the capacity + /// returned by `alloc_excess` locally, this method is likely to + /// produce useful results. + #[inline] + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + (layout.size(), layout.size()) + } + + // == METHODS FOR MEMORY REUSE == + // realloc. alloc_excess, realloc_excess + + /// Returns a pointer suitable for holding data described by + /// `new_layout`, meeting its size and alignment guarantees. To + /// accomplish this, this may extend or shrink the allocation + /// referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then ownership of the memory block + /// referenced by `ptr` has been transferred to this + /// allocator. The memory may or may not have been freed, and + /// should be considered unusable (unless of course it was + /// transferred back to the caller again via the return value of + /// this method). + /// + /// If this method returns `Err`, then ownership of the memory + /// block has not been transferred to this allocator, and the + /// contents of the memory block are unaltered. + /// + /// For best results, `new_layout` should not impose a different + /// alignment constraint than `layout`. (In other words, + /// `new_layout.align()` should equal `layout.align()`.) However, + /// behavior is well-defined (though underspecified) when this + /// constraint is violated; further discussion below. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` + /// argument need not fit it.) + /// + /// * `new_layout` must have size greater than zero. + /// + /// * the alignment of `new_layout` is non-zero. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returns `Err` only if `new_layout` does not match the + /// alignment of `layout`, or does not meet the allocator's size + /// and alignment constraints of the allocator, or if reallocation + /// otherwise fails. + /// + /// (Note the previous sentence did not say "if and only if" -- in + /// particular, an implementation of this method *can* return `Ok` + /// if `new_layout.align() != old_layout.align()`; or it can + /// return `Err` in that scenario, depending on whether this + /// allocator can dynamically adjust the alignment constraint for + /// the block.) + /// + /// Implementations are encouraged to return `Err` on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + let new_size = new_layout.size(); + let old_size = layout.size(); + let aligns_match = layout.align == new_layout.align; + + if new_size >= old_size && aligns_match { + if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { + return Ok(ptr); + } + } else if new_size < old_size && aligns_match { + if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { + return Ok(ptr); + } + } + + // otherwise, fall back on alloc + copy + dealloc. + let result = self.alloc(new_layout); + if let Ok(new_ptr) = result { + ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); + self.dealloc(ptr, layout); + } + result + } + + /// Behaves like `alloc`, but also ensures that the contents + /// are set to zero before being returned. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + let size = layout.size(); + let p = self.alloc(layout); + if let Ok(p) = p { + ptr::write_bytes(p, 0, size); + } + p + } + + /// Behaves like `alloc`, but also returns the whole size of + /// the returned block. For some `layout` inputs, like arrays, this + /// may include extra storage usable for additional data. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + let usable_size = self.usable_size(&layout); + self.alloc(layout).map(|p| Excess(p, usable_size.1)) + } + + /// Behaves like `realloc`, but also returns the whole size of + /// the returned block. For some `layout` inputs, like arrays, this + /// may include extra storage usable for additional data. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `realloc` is. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `layout` does not meet allocator's size or alignment + /// constraints, just as in `realloc`. + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + let usable_size = self.usable_size(&new_layout); + self.realloc(ptr, layout, new_layout) + .map(|p| Excess(p, usable_size.1)) + } + + /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_layout`, and thus can + /// be used to carry data of that layout. (The allocator is allowed to + /// expend effort to accomplish this, such as extending the memory block to + /// include successor blocks, or virtual memory tricks.) + /// + /// Regardless of what this method returns, ownership of the + /// memory block referenced by `ptr` has not been transferred, and + /// the contents of the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_layout` argument need not fit it, + /// + /// * `new_layout.size()` must not be less than `layout.size()`, + /// + /// * `new_layout.align()` must equal `layout.align()`. + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `grow_in_place` failures without aborting, or to fall back on + /// another reallocation method before resorting to an abort. + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_layout.size >= layout.size); + debug_assert!(new_layout.align == layout.align); + let (_l, u) = self.usable_size(&layout); + // _l <= layout.size() [guaranteed by usable_size()] + // layout.size() <= new_layout.size() [required by this method] + if new_layout.size <= u { + return Ok(()); + } else { + return Err(CannotReallocInPlace); + } + } + + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. + /// + /// If this returns `Ok`, then the allocator has asserted that the + /// memory block referenced by `ptr` now fits `new_layout`, and + /// thus can only be used to carry data of that smaller + /// layout. (The allocator is allowed to take advantage of this, + /// carving off portions of the block for reuse elsewhere.) The + /// truncated contents of the block within the smaller layout are + /// unaltered, and ownership of block has not been transferred. + /// + /// If this returns `Err`, then the memory block is considered to + /// still represent the original (larger) `layout`. None of the + /// block has been carved off for reuse elsewhere, ownership of + /// the memory block has not been transferred, and the contents of + /// the memory block are unaltered. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must *fit* the `ptr` (see above); note the + /// `new_layout` argument need not fit it, + /// + /// * `new_layout.size()` must not be greater than `layout.size()` + /// (and must be greater than zero), + /// + /// * `new_layout.align()` must equal `layout.align()`. + /// + /// # Errors + /// + /// Returns `Err(CannotReallocInPlace)` when the allocator is + /// unable to assert that the memory block referenced by `ptr` + /// could fit `layout`. + /// + /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// method; clients are expected either to be able to recover from + /// `shrink_in_place` failures without aborting, or to fall back + /// on another reallocation method before resorting to an abort. + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let _ = ptr; // this default implementation doesn't care about the actual address. + debug_assert!(new_layout.size <= layout.size); + debug_assert!(new_layout.align == layout.align); + let (l, _u) = self.usable_size(&layout); + // layout.size() <= _u [guaranteed by usable_size()] + // new_layout.size() <= layout.size() [required by this method] + if l <= new_layout.size { + return Ok(()); + } else { + return Err(CannotReallocInPlace); + } + } + + + // == COMMON USAGE PATTERNS == + // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array + + /// Allocates a block suitable for holding an instance of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` + /// must be considered "currently allocated" and must be + /// acceptable input to methods such as `realloc` or `dealloc`, + /// *even if* `T` is a zero-sized type. In other words, if your + /// `Alloc` implementation overrides this method in a manner + /// that can return a zero-sized `ptr`, then all reallocation and + /// deallocation methods need to be similarly overridden to accept + /// such values as input. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `T` does not meet allocator's size or alignment constraints. + /// + /// For zero-sized `T`, may return either of `Ok` or `Err`, but + /// will *not* yield undefined behavior. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + fn alloc_one(&mut self) -> Result, AllocErr> + where Self: Sized + { + let k = Layout::new::(); + if k.size() > 0 { + unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } + } else { + Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) + } + } + + /// Deallocates a block suitable for holding an instance of `T`. + /// + /// The given block must have been produced by this allocator, + /// and must be suitable for storing a `T` (in terms of alignment + /// as well as minimum and maximum size); otherwise yields + /// undefined behavior. + /// + /// Captures a common usage pattern for allocators. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure both: + /// + /// * `ptr` must denote a block of memory currently allocated via this allocator + /// + /// * the layout of `T` must *fit* that block of memory. + unsafe fn dealloc_one(&mut self, ptr: NonNull) + where Self: Sized + { + let raw_ptr = ptr.as_ptr() as *mut u8; + let k = Layout::new::(); + if k.size() > 0 { + self.dealloc(raw_ptr, k); + } + } + + /// Allocates a block suitable for holding `n` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` + /// must be considered "currently allocated" and must be + /// acceptable input to methods such as `realloc` or `dealloc`, + /// *even if* `T` is a zero-sized type. In other words, if your + /// `Alloc` implementation overrides this method in a manner + /// that can return a zero-sized `ptr`, then all reallocation and + /// deallocation methods need to be similarly overridden to accept + /// such values as input. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `[T; n]` does not meet allocator's size or alignment + /// constraints. + /// + /// For zero-sized `T` or `n == 0`, may return either of `Ok` or + /// `Err`, but will *not* yield undefined behavior. + /// + /// Always returns `Err` on arithmetic overflow. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + fn alloc_array(&mut self, n: usize) -> Result, AllocErr> + where Self: Sized + { + match Layout::array::(n) { + Some(ref layout) if layout.size() > 0 => { + unsafe { + self.alloc(layout.clone()) + .map(|p| { + NonNull::new_unchecked(p as *mut T) + }) + } + } + _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), + } + } + + /// Reallocates a block previously suitable for holding `n_old` + /// instances of `T`, returning a block suitable for holding + /// `n_new` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// The returned block is suitable for passing to the + /// `alloc`/`realloc` methods of this allocator. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * the layout of `[T; n_old]` must *fit* that block of memory. + /// + /// # Errors + /// + /// Returning `Err` indicates that either memory is exhausted or + /// `[T; n_new]` does not meet allocator's size or alignment + /// constraints. + /// + /// For zero-sized `T` or `n_new == 0`, may return either of `Ok` or + /// `Err`, but will *not* yield undefined behavior. + /// + /// Always returns `Err` on arithmetic overflow. + /// + /// Clients wishing to abort computation in response to an + /// reallocation error are encouraged to call the allocator's `oom` + /// method, rather than directly invoking `panic!` or similar. + unsafe fn realloc_array(&mut self, + ptr: NonNull, + n_old: usize, + n_new: usize) -> Result, AllocErr> + where Self: Sized + { + match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { + (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { + self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) + .map(|p| NonNull::new_unchecked(p as *mut T)) + } + _ => { + Err(AllocErr::invalid_input("invalid layout for realloc_array")) + } + } + } + + /// Deallocates a block suitable for holding `n` instances of `T`. + /// + /// Captures a common usage pattern for allocators. + /// + /// # Safety + /// + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure both: + /// + /// * `ptr` must denote a block of memory currently allocated via this allocator + /// + /// * the layout of `[T; n]` must *fit* that block of memory. + /// + /// # Errors + /// + /// Returning `Err` indicates that either `[T; n]` or the given + /// memory block does not meet allocator's size or alignment + /// constraints. + /// + /// Always returns `Err` on arithmetic overflow. + unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> + where Self: Sized + { + let raw_ptr = ptr.as_ptr() as *mut u8; + match Layout::array::(n) { + Some(ref k) if k.size() > 0 => { + Ok(self.dealloc(raw_ptr, k.clone())) + } + _ => { + Err(AllocErr::invalid_input("invalid layout for dealloc_array")) + } + } + } +} diff --git a/src/libcore/heap.rs b/src/libcore/heap.rs deleted file mode 100644 index 5c51bb2b51b..00000000000 --- a/src/libcore/heap.rs +++ /dev/null @@ -1,1125 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] - -use cmp; -use fmt; -use mem; -use usize; -use ptr::{self, NonNull}; - -extern { - /// An opaque, unsized type. Used for pointers to allocated memory. - /// - /// This type can only be used behind a pointer like `*mut Void` or `ptr::NonNull`. - /// Such pointers are similar to C’s `void*` type. - pub type Void; -} - -impl Void { - /// Similar to `std::ptr::null`, which requires `T: Sized`. - pub fn null() -> *const Self { - 0 as _ - } - - /// Similar to `std::ptr::null_mut`, which requires `T: Sized`. - pub fn null_mut() -> *mut Self { - 0 as _ - } -} - -/// Represents the combination of a starting address and -/// a total capacity of the returned block. -#[derive(Debug)] -pub struct Excess(pub *mut u8, pub usize); - -fn size_align() -> (usize, usize) { - (mem::size_of::(), mem::align_of::()) -} - -/// Layout of a block of memory. -/// -/// An instance of `Layout` describes a particular layout of memory. -/// You build a `Layout` up as an input to give to an allocator. -/// -/// All layouts have an associated non-negative size and a -/// power-of-two alignment. -/// -/// (Note however that layouts are *not* required to have positive -/// size, even though many allocators require that all memory -/// requests have positive size. A caller to the `Alloc::alloc` -/// method must either ensure that conditions like this are met, or -/// use specific allocators with looser requirements.) -#[derive(Clone, Debug, PartialEq, Eq)] -pub struct Layout { - // size of the requested block of memory, measured in bytes. - size: usize, - - // alignment of the requested block of memory, measured in bytes. - // we ensure that this is always a power-of-two, because API's - // like `posix_memalign` require it and it is a reasonable - // constraint to impose on Layout constructors. - // - // (However, we do not analogously require `align >= sizeof(void*)`, - // even though that is *also* a requirement of `posix_memalign`.) - align: usize, -} - - -// FIXME: audit default implementations for overflow errors, -// (potentially switching to overflowing_add and -// overflowing_mul as necessary). - -impl Layout { - /// Constructs a `Layout` from a given `size` and `align`, - /// or returns `None` if either of the following conditions - /// are not met: - /// - /// * `align` must be a power of two, - /// - /// * `size`, when rounded up to the nearest multiple of `align`, - /// must not overflow (i.e. the rounded value must be less than - /// `usize::MAX`). - #[inline] - pub fn from_size_align(size: usize, align: usize) -> Option { - if !align.is_power_of_two() { - return None; - } - - // (power-of-two implies align != 0.) - - // Rounded up size is: - // size_rounded_up = (size + align - 1) & !(align - 1); - // - // We know from above that align != 0. If adding (align - 1) - // does not overflow, then rounding up will be fine. - // - // Conversely, &-masking with !(align - 1) will subtract off - // only low-order-bits. Thus if overflow occurs with the sum, - // the &-mask cannot subtract enough to undo that overflow. - // - // Above implies that checking for summation overflow is both - // necessary and sufficient. - if size > usize::MAX - (align - 1) { - return None; - } - - unsafe { - Some(Layout::from_size_align_unchecked(size, align)) - } - } - - /// Creates a layout, bypassing all checks. - /// - /// # Safety - /// - /// This function is unsafe as it does not verify that `align` is - /// a power-of-two nor `size` aligned to `align` fits within the - /// address space (i.e. the `Layout::from_size_align` preconditions). - #[inline] - pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { - Layout { size: size, align: align } - } - - /// The minimum size in bytes for a memory block of this layout. - #[inline] - pub fn size(&self) -> usize { self.size } - - /// The minimum byte alignment for a memory block of this layout. - #[inline] - pub fn align(&self) -> usize { self.align } - - /// Constructs a `Layout` suitable for holding a value of type `T`. - pub fn new() -> Self { - let (size, align) = size_align::(); - Layout::from_size_align(size, align).unwrap() - } - - /// Produces layout describing a record that could be used to - /// allocate backing structure for `T` (which could be a trait - /// or other unsized type like a slice). - pub fn for_value(t: &T) -> Self { - let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); - Layout::from_size_align(size, align).unwrap() - } - - /// Creates a layout describing the record that can hold a value - /// of the same layout as `self`, but that also is aligned to - /// alignment `align` (measured in bytes). - /// - /// If `self` already meets the prescribed alignment, then returns - /// `self`. - /// - /// Note that this method does not add any padding to the overall - /// size, regardless of whether the returned layout has a different - /// alignment. In other words, if `K` has size 16, `K.align_to(32)` - /// will *still* have size 16. - /// - /// # Panics - /// - /// Panics if the combination of `self.size` and the given `align` - /// violates the conditions listed in `from_size_align`. - #[inline] - pub fn align_to(&self, align: usize) -> Self { - Layout::from_size_align(self.size, cmp::max(self.align, align)).unwrap() - } - - /// Returns the amount of padding we must insert after `self` - /// to ensure that the following address will satisfy `align` - /// (measured in bytes). - /// - /// E.g. if `self.size` is 9, then `self.padding_needed_for(4)` - /// returns 3, because that is the minimum number of bytes of - /// padding required to get a 4-aligned address (assuming that the - /// corresponding memory block starts at a 4-aligned address). - /// - /// The return value of this function has no meaning if `align` is - /// not a power-of-two. - /// - /// Note that the utility of the returned value requires `align` - /// to be less than or equal to the alignment of the starting - /// address for the whole allocated block of memory. One way to - /// satisfy this constraint is to ensure `align <= self.align`. - #[inline] - pub fn padding_needed_for(&self, align: usize) -> usize { - let len = self.size(); - - // Rounded up value is: - // len_rounded_up = (len + align - 1) & !(align - 1); - // and then we return the padding difference: `len_rounded_up - len`. - // - // We use modular arithmetic throughout: - // - // 1. align is guaranteed to be > 0, so align - 1 is always - // valid. - // - // 2. `len + align - 1` can overflow by at most `align - 1`, - // so the &-mask wth `!(align - 1)` will ensure that in the - // case of overflow, `len_rounded_up` will itself be 0. - // Thus the returned padding, when added to `len`, yields 0, - // which trivially satisfies the alignment `align`. - // - // (Of course, attempts to allocate blocks of memory whose - // size and padding overflow in the above manner should cause - // the allocator to yield an error anyway.) - - let len_rounded_up = len.wrapping_add(align).wrapping_sub(1) & !align.wrapping_sub(1); - return len_rounded_up.wrapping_sub(len); - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with a suitable amount of padding between each to - /// ensure that each instance is given its requested size and - /// alignment. On success, returns `(k, offs)` where `k` is the - /// layout of the array and `offs` is the distance between the start - /// of each element in the array. - /// - /// On arithmetic overflow, returns `None`. - #[inline] - pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { - let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?; - let alloc_size = padded_size.checked_mul(n)?; - - // We can assume that `self.align` is a power-of-two. - // Furthermore, `alloc_size` has already been rounded up - // to a multiple of `self.align`; therefore, the call to - // `Layout::from_size_align` below should never panic. - Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) - } - - /// Creates a layout describing the record for `self` followed by - /// `next`, including any necessary padding to ensure that `next` - /// will be properly aligned. Note that the result layout will - /// satisfy the alignment properties of both `self` and `next`. - /// - /// Returns `Some((k, offset))`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// On arithmetic overflow, returns `None`. - pub fn extend(&self, next: Self) -> Option<(Self, usize)> { - let new_align = cmp::max(self.align, next.align); - let realigned = Layout::from_size_align(self.size, new_align)?; - - let pad = realigned.padding_needed_for(next.align); - - let offset = self.size.checked_add(pad)?; - let new_size = offset.checked_add(next.size)?; - - let layout = Layout::from_size_align(new_size, new_align)?; - Some((layout, offset)) - } - - /// Creates a layout describing the record for `n` instances of - /// `self`, with no padding between each instance. - /// - /// Note that, unlike `repeat`, `repeat_packed` does not guarantee - /// that the repeated instances of `self` will be properly - /// aligned, even if a given instance of `self` is properly - /// aligned. In other words, if the layout returned by - /// `repeat_packed` is used to allocate an array, it is not - /// guaranteed that all elements in the array will be properly - /// aligned. - /// - /// On arithmetic overflow, returns `None`. - pub fn repeat_packed(&self, n: usize) -> Option { - let size = self.size().checked_mul(n)?; - Layout::from_size_align(size, self.align) - } - - /// Creates a layout describing the record for `self` followed by - /// `next` with no additional padding between the two. Since no - /// padding is inserted, the alignment of `next` is irrelevant, - /// and is not incorporated *at all* into the resulting layout. - /// - /// Returns `(k, offset)`, where `k` is layout of the concatenated - /// record and `offset` is the relative location, in bytes, of the - /// start of the `next` embedded within the concatenated record - /// (assuming that the record itself starts at offset 0). - /// - /// (The `offset` is always the same as `self.size()`; we use this - /// signature out of convenience in matching the signature of - /// `extend`.) - /// - /// On arithmetic overflow, returns `None`. - pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { - let new_size = self.size().checked_add(next.size())?; - let layout = Layout::from_size_align(new_size, self.align)?; - Some((layout, self.size())) - } - - /// Creates a layout describing the record for a `[T; n]`. - /// - /// On arithmetic overflow, returns `None`. - pub fn array(n: usize) -> Option { - Layout::new::() - .repeat(n) - .map(|(k, offs)| { - debug_assert!(offs == mem::size_of::()); - k - }) - } -} - -/// The `AllocErr` error specifies whether an allocation failure is -/// specifically due to resource exhaustion or if it is due to -/// something wrong when combining the given input arguments with this -/// allocator. -#[derive(Clone, PartialEq, Eq, Debug)] -pub enum AllocErr { - /// Error due to hitting some resource limit or otherwise running - /// out of memory. This condition strongly implies that *some* - /// series of deallocations would allow a subsequent reissuing of - /// the original allocation request to succeed. - Exhausted { request: Layout }, - - /// Error due to allocator being fundamentally incapable of - /// satisfying the original request. This condition implies that - /// such an allocation request will never succeed on the given - /// allocator, regardless of environment, memory pressure, or - /// other contextual conditions. - /// - /// For example, an allocator that does not support requests for - /// large memory blocks might return this error variant. - Unsupported { details: &'static str }, -} - -impl AllocErr { - #[inline] - pub fn invalid_input(details: &'static str) -> Self { - AllocErr::Unsupported { details: details } - } - #[inline] - pub fn is_memory_exhausted(&self) -> bool { - if let AllocErr::Exhausted { .. } = *self { true } else { false } - } - #[inline] - pub fn is_request_unsupported(&self) -> bool { - if let AllocErr::Unsupported { .. } = *self { true } else { false } - } - #[inline] - pub fn description(&self) -> &str { - match *self { - AllocErr::Exhausted { .. } => "allocator memory exhausted", - AllocErr::Unsupported { .. } => "unsupported allocator request", - } - } -} - -// (we need this for downstream impl of trait Error) -impl fmt::Display for AllocErr { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// The `CannotReallocInPlace` error is used when `grow_in_place` or -/// `shrink_in_place` were unable to reuse the given memory block for -/// a requested layout. -#[derive(Clone, PartialEq, Eq, Debug)] -pub struct CannotReallocInPlace; - -impl CannotReallocInPlace { - pub fn description(&self) -> &str { - "cannot reallocate allocator's memory in place" - } -} - -// (we need this for downstream impl of trait Error) -impl fmt::Display for CannotReallocInPlace { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) - } -} - -/// Augments `AllocErr` with a CapacityOverflow variant. -#[derive(Clone, PartialEq, Eq, Debug)] -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -pub enum CollectionAllocErr { - /// Error due to the computed capacity exceeding the collection's maximum - /// (usually `isize::MAX` bytes). - CapacityOverflow, - /// Error due to the allocator (see the `AllocErr` type's docs). - AllocErr(AllocErr), -} - -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -impl From for CollectionAllocErr { - fn from(err: AllocErr) -> Self { - CollectionAllocErr::AllocErr(err) - } -} - -// FIXME: docs -pub unsafe trait GlobalAlloc { - unsafe fn alloc(&self, layout: Layout) -> *mut Void; - - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout); - - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { - let size = layout.size(); - let ptr = self.alloc(layout); - if !ptr.is_null() { - ptr::write_bytes(ptr as *mut u8, 0, size); - } - ptr - } - - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - let new_ptr = self.alloc(new_layout); - if !new_ptr.is_null() { - ptr::copy_nonoverlapping( - ptr as *const u8, - new_ptr as *mut u8, - cmp::min(old_layout.size(), new_size), - ); - self.dealloc(ptr, old_layout); - } - new_ptr - } -} - -/// An implementation of `Alloc` can allocate, reallocate, and -/// deallocate arbitrary blocks of data described via `Layout`. -/// -/// Some of the methods require that a memory block be *currently -/// allocated* via an allocator. This means that: -/// -/// * the starting address for that memory block was previously -/// returned by a previous call to an allocation method (`alloc`, -/// `alloc_zeroed`, `alloc_excess`, `alloc_one`, `alloc_array`) or -/// reallocation method (`realloc`, `realloc_excess`, or -/// `realloc_array`), and -/// -/// * the memory block has not been subsequently deallocated, where -/// blocks are deallocated either by being passed to a deallocation -/// method (`dealloc`, `dealloc_one`, `dealloc_array`) or by being -/// passed to a reallocation method (see above) that returns `Ok`. -/// -/// A note regarding zero-sized types and zero-sized layouts: many -/// methods in the `Alloc` trait state that allocation requests -/// must be non-zero size, or else undefined behavior can result. -/// -/// * However, some higher-level allocation methods (`alloc_one`, -/// `alloc_array`) are well-defined on zero-sized types and can -/// optionally support them: it is left up to the implementor -/// whether to return `Err`, or to return `Ok` with some pointer. -/// -/// * If an `Alloc` implementation chooses to return `Ok` in this -/// case (i.e. the pointer denotes a zero-sized inaccessible block) -/// then that returned pointer must be considered "currently -/// allocated". On such an allocator, *all* methods that take -/// currently-allocated pointers as inputs must accept these -/// zero-sized pointers, *without* causing undefined behavior. -/// -/// * In other words, if a zero-sized pointer can flow out of an -/// allocator, then that allocator must likewise accept that pointer -/// flowing back into its deallocation and reallocation methods. -/// -/// Some of the methods require that a layout *fit* a memory block. -/// What it means for a layout to "fit" a memory block means (or -/// equivalently, for a memory block to "fit" a layout) is that the -/// following two conditions must hold: -/// -/// 1. The block's starting address must be aligned to `layout.align()`. -/// -/// 2. The block's size must fall in the range `[use_min, use_max]`, where: -/// -/// * `use_min` is `self.usable_size(layout).0`, and -/// -/// * `use_max` is the capacity that was (or would have been) -/// returned when (if) the block was allocated via a call to -/// `alloc_excess` or `realloc_excess`. -/// -/// Note that: -/// -/// * the size of the layout most recently used to allocate the block -/// is guaranteed to be in the range `[use_min, use_max]`, and -/// -/// * a lower-bound on `use_max` can be safely approximated by a call to -/// `usable_size`. -/// -/// * 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 - // behavior in response to a zero size allocation request ; - // e.g. in C, `malloc` of 0 will either return a null pointer or a - // unique pointer, but will not have arbitrary undefined - // behavior. Rust should consider revising the alloc::heap crate - // to reflect this reality.) - - /// Returns a pointer meeting the size and alignment guarantees of - /// `layout`. - /// - /// If this method returns an `Ok(addr)`, then the `addr` returned - /// will be non-null address pointing to a block of storage - /// suitable for holding an instance of `layout`. - /// - /// The returned block of storage may or may not have its contents - /// initialized. (Extension subtraits might restrict this - /// behavior, e.g. to ensure initialization to particular sets of - /// bit patterns.) - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure that `layout` has non-zero size. - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g. guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints. - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; - - /// Deallocate the memory referenced by `ptr`. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must denote a block of memory currently allocated via - /// this allocator, - /// - /// * `layout` must *fit* that block of memory, - /// - /// * In addition to fitting the block of memory `layout`, the - /// alignment of the `layout` must match the alignment used - /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); - - /// Allocator-specific method for signaling an out-of-memory - /// condition. - /// - /// `oom` aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request (signaled by an error such as - /// `AllocErr::Exhausted`), and wish to abandon computation rather - /// than attempt to recover locally. Such clients should pass the - /// signaling error value back into `oom`, where the allocator - /// may incorporate that error value into its diagnostic report - /// before aborting. - /// - /// Implementations of the `oom` method are discouraged from - /// infinitely regressing in nested calls to `oom`. In - /// practice this means implementors should eschew allocating, - /// especially from `self` (directly or indirectly). - /// - /// Implementations of the allocation and reallocation methods - /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from - /// panicking (or aborting) in the event of memory exhaustion; - /// instead they should return an appropriate error from the - /// invoked method, and let the client decide whether to invoke - /// this `oom` method in response. - fn oom(&mut self, _: AllocErr) -> ! { - unsafe { ::intrinsics::abort() } - } - - // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == - // usable_size - - /// Returns bounds on the guaranteed usable size of a successful - /// allocation created with the specified `layout`. - /// - /// In particular, if one has a memory block allocated via a given - /// allocator `a` and layout `k` where `a.usable_size(k)` returns - /// `(l, u)`, then one can pass that block to `a.dealloc()` with a - /// layout in the size range [l, u]. - /// - /// (All implementors of `usable_size` must ensure that - /// `l <= k.size() <= u`) - /// - /// Both the lower- and upper-bounds (`l` and `u` respectively) - /// are provided, because an allocator based on size classes could - /// misbehave if one attempts to deallocate a block without - /// providing a correct value for its size (i.e., one within the - /// range `[l, u]`). - /// - /// Clients who wish to make use of excess capacity are encouraged - /// to use the `alloc_excess` and `realloc_excess` instead, as - /// this method is constrained to report conservative values that - /// serve as valid bounds for *all possible* allocation method - /// calls. - /// - /// However, for clients that do not wish to track the capacity - /// returned by `alloc_excess` locally, this method is likely to - /// produce useful results. - #[inline] - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - (layout.size(), layout.size()) - } - - // == METHODS FOR MEMORY REUSE == - // realloc. alloc_excess, realloc_excess - - /// Returns a pointer suitable for holding data described by - /// `new_layout`, meeting its size and alignment guarantees. To - /// accomplish this, this may extend or shrink the allocation - /// referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then ownership of the memory block - /// referenced by `ptr` has been transferred to this - /// allocator. The memory may or may not have been freed, and - /// should be considered unusable (unless of course it was - /// transferred back to the caller again via the return value of - /// this method). - /// - /// If this method returns `Err`, then ownership of the memory - /// block has not been transferred to this allocator, and the - /// contents of the memory block are unaltered. - /// - /// For best results, `new_layout` should not impose a different - /// alignment constraint than `layout`. (In other words, - /// `new_layout.align()` should equal `layout.align()`.) However, - /// behavior is well-defined (though underspecified) when this - /// constraint is violated; further discussion below. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` - /// argument need not fit it.) - /// - /// * `new_layout` must have size greater than zero. - /// - /// * the alignment of `new_layout` is non-zero. - /// - /// (Extension subtraits might provide more specific bounds on - /// behavior, e.g. guarantee a sentinel address or a null pointer - /// in response to a zero-size allocation request.) - /// - /// # Errors - /// - /// Returns `Err` only if `new_layout` does not match the - /// alignment of `layout`, or does not meet the allocator's size - /// and alignment constraints of the allocator, or if reallocation - /// otherwise fails. - /// - /// (Note the previous sentence did not say "if and only if" -- in - /// particular, an implementation of this method *can* return `Ok` - /// if `new_layout.align() != old_layout.align()`; or it can - /// return `Err` in that scenario, depending on whether this - /// allocator can dynamically adjust the alignment constraint for - /// the block.) - /// - /// Implementations are encouraged to return `Err` on memory - /// exhaustion rather than panicking or aborting, but this is not - /// a strict requirement. (Specifically: it is *legal* to - /// implement this trait atop an underlying native allocation - /// library that aborts on memory exhaustion.) - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - let new_size = new_layout.size(); - let old_size = layout.size(); - let aligns_match = layout.align == new_layout.align; - - if new_size >= old_size && aligns_match { - if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { - return Ok(ptr); - } - } else if new_size < old_size && aligns_match { - if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { - return Ok(ptr); - } - } - - // otherwise, fall back on alloc + copy + dealloc. - let result = self.alloc(new_layout); - if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); - self.dealloc(ptr, layout); - } - result - } - - /// Behaves like `alloc`, but also ensures that the contents - /// are set to zero before being returned. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `alloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let size = layout.size(); - let p = self.alloc(layout); - if let Ok(p) = p { - ptr::write_bytes(p, 0, size); - } - p - } - - /// Behaves like `alloc`, but also returns the whole size of - /// the returned block. For some `layout` inputs, like arrays, this - /// may include extra storage usable for additional data. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `alloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `alloc`. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let usable_size = self.usable_size(&layout); - self.alloc(layout).map(|p| Excess(p, usable_size.1)) - } - - /// Behaves like `realloc`, but also returns the whole size of - /// the returned block. For some `layout` inputs, like arrays, this - /// may include extra storage usable for additional data. - /// - /// # Safety - /// - /// This function is unsafe for the same reasons that `realloc` is. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `layout` does not meet allocator's size or alignment - /// constraints, just as in `realloc`. - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let usable_size = self.usable_size(&new_layout); - self.realloc(ptr, layout, new_layout) - .map(|p| Excess(p, usable_size.1)) - } - - /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and thus can - /// be used to carry data of that layout. (The allocator is allowed to - /// expend effort to accomplish this, such as extending the memory block to - /// include successor blocks, or virtual memory tricks.) - /// - /// Regardless of what this method returns, ownership of the - /// memory block referenced by `ptr` has not been transferred, and - /// the contents of the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, - /// - /// * `new_layout.size()` must not be less than `layout.size()`, - /// - /// * `new_layout.align()` must equal `layout.align()`. - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from - /// `grow_in_place` failures without aborting, or to fall back on - /// another reallocation method before resorting to an abort. - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size >= layout.size); - debug_assert!(new_layout.align == layout.align); - let (_l, u) = self.usable_size(&layout); - // _l <= layout.size() [guaranteed by usable_size()] - // layout.size() <= new_layout.size() [required by this method] - if new_layout.size <= u { - return Ok(()); - } else { - return Err(CannotReallocInPlace); - } - } - - /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. - /// - /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and - /// thus can only be used to carry data of that smaller - /// layout. (The allocator is allowed to take advantage of this, - /// carving off portions of the block for reuse elsewhere.) The - /// truncated contents of the block within the smaller layout are - /// unaltered, and ownership of block has not been transferred. - /// - /// If this returns `Err`, then the memory block is considered to - /// still represent the original (larger) `layout`. None of the - /// block has been carved off for reuse elsewhere, ownership of - /// the memory block has not been transferred, and the contents of - /// the memory block are unaltered. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, - /// - /// * `new_layout.size()` must not be greater than `layout.size()` - /// (and must be greater than zero), - /// - /// * `new_layout.align()` must equal `layout.align()`. - /// - /// # Errors - /// - /// Returns `Err(CannotReallocInPlace)` when the allocator is - /// unable to assert that the memory block referenced by `ptr` - /// could fit `layout`. - /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from - /// `shrink_in_place` failures without aborting, or to fall back - /// on another reallocation method before resorting to an abort. - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size <= layout.size); - debug_assert!(new_layout.align == layout.align); - let (l, _u) = self.usable_size(&layout); - // layout.size() <= _u [guaranteed by usable_size()] - // new_layout.size() <= layout.size() [required by this method] - if l <= new_layout.size { - return Ok(()); - } else { - return Err(CannotReallocInPlace); - } - } - - - // == COMMON USAGE PATTERNS == - // alloc_one, dealloc_one, alloc_array, realloc_array. dealloc_array - - /// Allocates a block suitable for holding an instance of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` - /// must be considered "currently allocated" and must be - /// acceptable input to methods such as `realloc` or `dealloc`, - /// *even if* `T` is a zero-sized type. In other words, if your - /// `Alloc` implementation overrides this method in a manner - /// that can return a zero-sized `ptr`, then all reallocation and - /// deallocation methods need to be similarly overridden to accept - /// such values as input. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `T` does not meet allocator's size or alignment constraints. - /// - /// For zero-sized `T`, may return either of `Ok` or `Err`, but - /// will *not* yield undefined behavior. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - fn alloc_one(&mut self) -> Result, AllocErr> - where Self: Sized - { - let k = Layout::new::(); - if k.size() > 0 { - unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } - } else { - Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) - } - } - - /// Deallocates a block suitable for holding an instance of `T`. - /// - /// The given block must have been produced by this allocator, - /// and must be suitable for storing a `T` (in terms of alignment - /// as well as minimum and maximum size); otherwise yields - /// undefined behavior. - /// - /// Captures a common usage pattern for allocators. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure both: - /// - /// * `ptr` must denote a block of memory currently allocated via this allocator - /// - /// * the layout of `T` must *fit* that block of memory. - unsafe fn dealloc_one(&mut self, ptr: NonNull) - where Self: Sized - { - let raw_ptr = ptr.as_ptr() as *mut u8; - let k = Layout::new::(); - if k.size() > 0 { - self.dealloc(raw_ptr, k); - } - } - - /// Allocates a block suitable for holding `n` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// Note to implementors: If this returns `Ok(ptr)`, then `ptr` - /// must be considered "currently allocated" and must be - /// acceptable input to methods such as `realloc` or `dealloc`, - /// *even if* `T` is a zero-sized type. In other words, if your - /// `Alloc` implementation overrides this method in a manner - /// that can return a zero-sized `ptr`, then all reallocation and - /// deallocation methods need to be similarly overridden to accept - /// such values as input. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `[T; n]` does not meet allocator's size or alignment - /// constraints. - /// - /// For zero-sized `T` or `n == 0`, may return either of `Ok` or - /// `Err`, but will *not* yield undefined behavior. - /// - /// Always returns `Err` on arithmetic overflow. - /// - /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - fn alloc_array(&mut self, n: usize) -> Result, AllocErr> - where Self: Sized - { - match Layout::array::(n) { - Some(ref layout) if layout.size() > 0 => { - unsafe { - self.alloc(layout.clone()) - .map(|p| { - NonNull::new_unchecked(p as *mut T) - }) - } - } - _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), - } - } - - /// Reallocates a block previously suitable for holding `n_old` - /// instances of `T`, returning a block suitable for holding - /// `n_new` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// The returned block is suitable for passing to the - /// `alloc`/`realloc` methods of this allocator. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure all of the following: - /// - /// * `ptr` must be currently allocated via this allocator, - /// - /// * the layout of `[T; n_old]` must *fit* that block of memory. - /// - /// # Errors - /// - /// Returning `Err` indicates that either memory is exhausted or - /// `[T; n_new]` does not meet allocator's size or alignment - /// constraints. - /// - /// For zero-sized `T` or `n_new == 0`, may return either of `Ok` or - /// `Err`, but will *not* yield undefined behavior. - /// - /// Always returns `Err` on arithmetic overflow. - /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. - unsafe fn realloc_array(&mut self, - ptr: NonNull, - n_old: usize, - n_new: usize) -> Result, AllocErr> - where Self: Sized - { - match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { - (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { - self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) - .map(|p| NonNull::new_unchecked(p as *mut T)) - } - _ => { - Err(AllocErr::invalid_input("invalid layout for realloc_array")) - } - } - } - - /// Deallocates a block suitable for holding `n` instances of `T`. - /// - /// Captures a common usage pattern for allocators. - /// - /// # Safety - /// - /// This function is unsafe because undefined behavior can result - /// if the caller does not ensure both: - /// - /// * `ptr` must denote a block of memory currently allocated via this allocator - /// - /// * the layout of `[T; n]` must *fit* that block of memory. - /// - /// # Errors - /// - /// Returning `Err` indicates that either `[T; n]` or the given - /// memory block does not meet allocator's size or alignment - /// constraints. - /// - /// Always returns `Err` on arithmetic overflow. - unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> - where Self: Sized - { - let raw_ptr = ptr.as_ptr() as *mut u8; - match Layout::array::(n) { - Some(ref k) if k.size() > 0 => { - Ok(self.dealloc(raw_ptr, k.clone())) - } - _ => { - Err(AllocErr::invalid_input("invalid layout for dealloc_array")) - } - } - } -} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 722a9de215c..56d4e65d3ac 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -185,7 +185,11 @@ pub mod unicode; /* Heap memory allocator trait */ #[allow(missing_docs)] -pub mod heap; +pub mod alloc; + +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] +pub use alloc as heap; // note: does not need to be public mod iter_private; diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs new file mode 100644 index 00000000000..b42a1052c49 --- /dev/null +++ b/src/libstd/alloc.rs @@ -0,0 +1,176 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! dox + +#![unstable(issue = "32838", feature = "allocator_api")] + +pub use alloc_crate::heap::Heap; +pub use alloc_system::System; +#[doc(inline)] pub use core::heap::*; + +#[cfg(not(test))] +#[doc(hidden)] +#[allow(unused_attributes)] +pub mod __default_lib_allocator { + use super::{System, Layout, Alloc, AllocErr}; + use ptr; + + // for symbol names src/librustc/middle/allocator.rs + // for signatures src/librustc_allocator/lib.rs + + // linkage directives are provided as part of the current compiler allocator + // ABI + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_alloc(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { + let layout = Layout::from_size_align_unchecked(size, align); + match System.alloc(layout) { + Ok(p) => p, + Err(e) => { + ptr::write(err as *mut AllocErr, e); + 0 as *mut u8 + } + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_oom(err: *const u8) -> ! { + System.oom((*(err as *const AllocErr)).clone()) + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, + size: usize, + align: usize) { + System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_usable_size(layout: *const u8, + min: *mut usize, + max: *mut usize) { + let pair = System.usable_size(&*(layout as *const Layout)); + *min = pair.0; + *max = pair.1; + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_realloc(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + err: *mut u8) -> *mut u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, old_align); + let new_layout = Layout::from_size_align_unchecked(new_size, new_align); + match System.realloc(ptr, old_layout, new_layout) { + Ok(p) => p, + Err(e) => { + ptr::write(err as *mut AllocErr, e); + 0 as *mut u8 + } + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_alloc_zeroed(size: usize, + align: usize, + err: *mut u8) -> *mut u8 { + let layout = Layout::from_size_align_unchecked(size, align); + match System.alloc_zeroed(layout) { + Ok(p) => p, + Err(e) => { + ptr::write(err as *mut AllocErr, e); + 0 as *mut u8 + } + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_alloc_excess(size: usize, + align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8 { + let layout = Layout::from_size_align_unchecked(size, align); + match System.alloc_excess(layout) { + Ok(p) => { + *excess = p.1; + p.0 + } + Err(e) => { + ptr::write(err as *mut AllocErr, e); + 0 as *mut u8 + } + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize, + excess: *mut usize, + err: *mut u8) -> *mut u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, old_align); + let new_layout = Layout::from_size_align_unchecked(new_size, new_align); + match System.realloc_excess(ptr, old_layout, new_layout) { + Ok(p) => { + *excess = p.1; + p.0 + } + Err(e) => { + ptr::write(err as *mut AllocErr, e); + 0 as *mut u8 + } + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, old_align); + let new_layout = Layout::from_size_align_unchecked(new_size, new_align); + match System.grow_in_place(ptr, old_layout, new_layout) { + Ok(()) => 1, + Err(_) => 0, + } + } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8, + old_size: usize, + old_align: usize, + new_size: usize, + new_align: usize) -> u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, old_align); + let new_layout = Layout::from_size_align_unchecked(new_size, new_align); + match System.shrink_in_place(ptr, old_layout, new_layout) { + Ok(()) => 1, + Err(_) => 0, + } + } +} diff --git a/src/libstd/heap.rs b/src/libstd/heap.rs deleted file mode 100644 index b42a1052c49..00000000000 --- a/src/libstd/heap.rs +++ /dev/null @@ -1,176 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! dox - -#![unstable(issue = "32838", feature = "allocator_api")] - -pub use alloc_crate::heap::Heap; -pub use alloc_system::System; -#[doc(inline)] pub use core::heap::*; - -#[cfg(not(test))] -#[doc(hidden)] -#[allow(unused_attributes)] -pub mod __default_lib_allocator { - use super::{System, Layout, Alloc, AllocErr}; - use ptr; - - // for symbol names src/librustc/middle/allocator.rs - // for signatures src/librustc_allocator/lib.rs - - // linkage directives are provided as part of the current compiler allocator - // ABI - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc(size: usize, - align: usize, - err: *mut u8) -> *mut u8 { - let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc(layout) { - Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom(err: *const u8) -> ! { - System.oom((*(err as *const AllocErr)).clone()) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, - size: usize, - align: usize) { - System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize) { - let pair = System.usable_size(&*(layout as *const Layout)); - *min = pair.0; - *max = pair.1; - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc(ptr, old_layout, new_layout) { - Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_zeroed(size: usize, - align: usize, - err: *mut u8) -> *mut u8 { - let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_zeroed(layout) { - Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_excess(layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc_excess(ptr, old_layout, new_layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.grow_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(_) => 0, - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.shrink_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(_) => 0, - } - } -} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index ef4205e7a62..3a99e845a16 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -477,7 +477,11 @@ pub mod path; pub mod process; pub mod sync; pub mod time; -pub mod heap; +pub mod alloc; + +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] +pub use alloc as heap; // Platform-abstraction modules #[macro_use] -- cgit 1.4.1-3-g733a5 From 743c29bdc5b0a75c648e1317aa5d1d816007f176 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 21:05:10 +0200 Subject: Actually deprecate heap modules. --- src/liballoc/alloc.rs | 2 +- src/liballoc/lib.rs | 10 ++++++++-- src/libcore/lib.rs | 5 ++++- src/libstd/alloc.rs | 6 +++--- src/libstd/lib.rs | 5 ++++- 5 files changed, 20 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 000c0123d9f..2477166966e 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -20,7 +20,7 @@ use core::mem::{self, ManuallyDrop}; use core::usize; #[doc(inline)] -pub use core::heap::*; +pub use core::alloc::*; #[doc(hidden)] pub mod __core { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 617bc5c52b3..066698a71df 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -141,7 +141,10 @@ mod macros; #[rustc_deprecated(since = "1.27.0", reason = "use the heap module in core, alloc, or std instead")] #[unstable(feature = "allocator_api", issue = "32838")] -pub use core::heap as allocator; +/// Use the `alloc` module instead. +pub mod allocator { + pub use alloc::*; +} // Heaps provided for low-level allocation strategies @@ -149,7 +152,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // Primitive types using the heaps above diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 56d4e65d3ac..5ebd9e4334c 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -189,7 +189,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // note: does not need to be public mod iter_private; diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index b42a1052c49..77be3e52d76 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,9 +12,9 @@ #![unstable(issue = "32838", feature = "allocator_api")] -pub use alloc_crate::heap::Heap; -pub use alloc_system::System; -#[doc(inline)] pub use core::heap::*; +#[doc(inline)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] pub use alloc_system::System; +#[doc(inline)] pub use core::alloc::*; #[cfg(not(test))] #[doc(hidden)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 3a99e845a16..25ba75fd35e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -481,7 +481,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -pub use alloc as heap; +/// Use the `alloc` module instead. +pub mod heap { + pub use alloc::*; +} // Platform-abstraction modules #[macro_use] -- cgit 1.4.1-3-g733a5 From 88ebd2d752831860d8824849cf6f5ae656a2c3eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 14:43:34 +0200 Subject: Rename the Heap type to Global MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … since it is the entry point for what’s registered with `#[global_allocator]` --- src/liballoc/alloc.rs | 23 ++++++++++++++--------- 1 file changed, 14 insertions(+), 9 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 2477166966e..1bd95cfd08c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -77,9 +77,14 @@ extern "Rust" { } #[derive(Copy, Clone, Default, Debug)] -pub struct Heap; +pub struct Global; -unsafe impl Alloc for Heap { +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] +pub use self::Global as Heap; + + +unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { let mut err = ManuallyDrop::new(mem::uninitialized::()); @@ -240,8 +245,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Heap.alloc(layout).unwrap_or_else(|err| { - Heap.oom(err) + Global.alloc(layout).unwrap_or_else(|err| { + Global.oom(err) }) } } @@ -254,7 +259,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Heap.dealloc(ptr as *mut u8, layout); + Global.dealloc(ptr as *mut u8, layout); } } @@ -263,14 +268,14 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use heap::{Heap, Alloc, Layout}; + use heap::{Global, Alloc, Layout}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); - let ptr = Heap.alloc_zeroed(layout.clone()) - .unwrap_or_else(|e| Heap.oom(e)); + let ptr = Global.alloc_zeroed(layout.clone()) + .unwrap_or_else(|e| Global.oom(e)); let end = ptr.offset(layout.size() as isize); let mut i = ptr; @@ -278,7 +283,7 @@ mod tests { assert_eq!(*i, 0); i = i.offset(1); } - Heap.dealloc(ptr, layout); + Global.dealloc(ptr, layout); } } -- cgit 1.4.1-3-g733a5 From e521b8b472dfe058f6d0f62f2e1ab5f291c220ee Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 21:15:06 +0200 Subject: Actually deprecate the Heap type --- src/liballoc/alloc.rs | 8 ++++++-- src/liballoc/arc.rs | 13 ++++++------- src/liballoc/btree/node.rs | 13 ++++++------- src/liballoc/raw_vec.rs | 23 +++++++++++------------ src/liballoc/rc.rs | 13 ++++++------- src/liballoc/tests/heap.rs | 4 ++-- src/libstd/alloc.rs | 3 ++- src/libstd/collections/hash/map.rs | 4 ++-- src/libstd/collections/hash/table.rs | 12 ++++++------ 9 files changed, 47 insertions(+), 46 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 1bd95cfd08c..12ee7701903 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -81,8 +81,12 @@ pub struct Global; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -pub use self::Global as Heap; +pub type Heap = Global; +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] +#[allow(non_upper_case_globals)] +pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] @@ -268,7 +272,7 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use heap::{Global, Alloc, Layout}; + use alloc::{Global, Alloc, Layout}; #[test] fn allocate_zeroed() { diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index ccf2e2768d1..d63ed24aa4f 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -21,7 +21,6 @@ use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; use core::fmt; use core::cmp::Ordering; -use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::mem::{self, align_of_val, size_of_val, uninitialized}; use core::ops::Deref; @@ -32,7 +31,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use heap::{Heap, box_free}; +use alloc::{Global, Alloc, Layout, box_free}; use boxed::Box; use string::String; use vec::Vec; @@ -521,7 +520,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) } } @@ -555,8 +554,8 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); - let mem = Heap.alloc(layout) - .unwrap_or_else(|e| Heap.oom(e)); + let mem = Global.alloc(layout) + .unwrap_or_else(|e| Global.oom(e)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; @@ -640,7 +639,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Heap.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem, self.layout.clone()); } } } @@ -1161,7 +1160,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 49109d522e9..8e23228bd28 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -41,14 +41,13 @@ // - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. // This implies that even an empty internal node has at least one edge. -use core::heap::{Alloc, Layout}; use core::marker::PhantomData; use core::mem; use core::ptr::{self, Unique, NonNull}; use core::slice; +use alloc::{Global, Alloc, Layout}; use boxed::Box; -use heap::Heap; const B: usize = 6; pub const MIN_LEN: usize = B - 1; @@ -250,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Heap.dealloc(top, Layout::new::>()); + Global.dealloc(top, Layout::new::>()); } } } @@ -436,7 +435,7 @@ impl NodeRef { > { let ptr = self.as_leaf() as *const LeafNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - Heap.dealloc(ptr, Layout::new::>()); + Global.dealloc(ptr, Layout::new::>()); ret } } @@ -457,7 +456,7 @@ impl NodeRef { > { let ptr = self.as_internal() as *const InternalNode as *const u8 as *mut u8; let ret = self.ascend().ok(); - Heap.dealloc(ptr, Layout::new::>()); + Global.dealloc(ptr, Layout::new::>()); ret } } @@ -1239,12 +1238,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: ).correct_parent_link(); } - Heap.dealloc( + Global.dealloc( right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); } else { - Heap.dealloc( + Global.dealloc( right_node.node.as_ptr() as *mut u8, Layout::new::>(), ); diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 3edce8aebdf..51f39dc6cc7 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,13 +8,12 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use alloc::{Alloc, Layout, Global}; use core::cmp; -use core::heap::{Alloc, Layout}; use core::mem; use core::ops::Drop; use core::ptr::{self, Unique}; use core::slice; -use heap::Heap; use super::boxed::Box; use super::allocator::CollectionAllocErr; use super::allocator::CollectionAllocErr::*; @@ -47,7 +46,7 @@ use super::allocator::CollectionAllocErr::*; /// field. This allows zero-sized types to not be special-cased by consumers of /// this type. #[allow(missing_debug_implementations)] -pub struct RawVec { +pub struct RawVec { ptr: Unique, cap: usize, a: A, @@ -114,14 +113,14 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Creates the biggest possible RawVec (on the system heap) /// without allocating. If T has positive size, then this makes a /// RawVec with capacity 0. If T has 0 size, then it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. pub fn new() -> Self { - Self::new_in(Heap) + Self::new_in(Global) } /// Creates a RawVec (on the system heap) with exactly the @@ -141,13 +140,13 @@ impl RawVec { /// Aborts on OOM #[inline] pub fn with_capacity(cap: usize) -> Self { - RawVec::allocate_in(cap, false, Heap) + RawVec::allocate_in(cap, false, Global) } /// Like `with_capacity` but guarantees the buffer is zeroed. #[inline] pub fn with_capacity_zeroed(cap: usize) -> Self { - RawVec::allocate_in(cap, true, Heap) + RawVec::allocate_in(cap, true, Global) } } @@ -168,7 +167,7 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Reconstitutes a RawVec from a pointer, capacity. /// /// # Undefined Behavior @@ -180,7 +179,7 @@ impl RawVec { RawVec { ptr: Unique::new_unchecked(ptr), cap, - a: Heap, + a: Global, } } @@ -678,7 +677,7 @@ impl RawVec { } } -impl RawVec { +impl RawVec { /// Converts the entire buffer into `Box<[T]>`. /// /// While it is not *strictly* Undefined Behavior to call @@ -763,13 +762,13 @@ mod tests { if size > self.fuel { return Err(AllocErr::Unsupported { details: "fuel exhausted" }); } - match Heap.alloc(layout) { + match Global.alloc(layout) { ok @ Ok(_) => { self.fuel -= size; ok } err @ Err(_) => err, } } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - Heap.dealloc(ptr, layout) + Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 8bdc57f96a6..c134b181158 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -250,7 +250,6 @@ use core::cell::Cell; use core::cmp::Ordering; use core::fmt; use core::hash::{Hash, Hasher}; -use core::heap::{Alloc, Layout}; use core::intrinsics::abort; use core::marker; use core::marker::{Unsize, PhantomData}; @@ -260,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use heap::{Heap, box_free}; +use alloc::{Global, Alloc, Layout, box_free}; use string::String; use vec::Vec; @@ -668,8 +667,8 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); - let mem = Heap.alloc(layout) - .unwrap_or_else(|e| Heap.oom(e)); + let mem = Global.alloc(layout) + .unwrap_or_else(|e| Global.oom(e)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; @@ -752,7 +751,7 @@ impl RcFromSlice for Rc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Heap.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem, self.layout.clone()); } } } @@ -847,7 +846,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } @@ -1273,7 +1272,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Heap.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); } } } diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs index d3ce12056bb..328131e2fef 100644 --- a/src/liballoc/tests/heap.rs +++ b/src/liballoc/tests/heap.rs @@ -9,7 +9,7 @@ // except according to those terms. use alloc_system::System; -use std::heap::{Heap, Alloc, Layout}; +use std::alloc::{Global, Alloc, Layout}; /// https://github.com/rust-lang/rust/issues/45955 /// @@ -22,7 +22,7 @@ fn alloc_system_overaligned_request() { #[test] fn std_heap_overaligned_request() { - check_overalign_requests(Heap) + check_overalign_requests(Global) } fn check_overalign_requests(mut allocator: T) { diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 77be3e52d76..eb0c960732d 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,7 +12,8 @@ #![unstable(issue = "32838", feature = "allocator_api")] -#[doc(inline)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; +#[doc(inline)] pub use alloc_crate::alloc::Global; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 73a5df8dc28..c4ef9e62577 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,13 +11,13 @@ use self::Entry::*; use self::VacantEntryState::*; +use alloc::{Global, Alloc, CollectionAllocErr}; use cell::Cell; use borrow::Borrow; use cmp::max; use fmt::{self, Debug}; #[allow(deprecated)] use hash::{Hash, Hasher, BuildHasher, SipHasher13}; -use heap::{Heap, Alloc, CollectionAllocErr}; use iter::{FromIterator, FusedIterator}; use mem::{self, replace}; use ops::{Deref, Index}; @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 878cd82a258..10bab5df8b5 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,9 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +use alloc::{Global, Alloc, Layout, CollectionAllocErr}; use cmp; use hash::{BuildHasher, Hash, Hasher}; -use heap::{Heap, Alloc, Layout, CollectionAllocErr}; use marker; use mem::{align_of, size_of, needs_drop}; use mem; @@ -754,7 +754,7 @@ impl RawTable { return Err(CollectionAllocErr::CapacityOverflow); } - let buffer = Heap.alloc(Layout::from_size_align(size, alignment) + let buffer = Global.alloc(Layout::from_size_align(size, alignment) .ok_or(CollectionAllocErr::CapacityOverflow)?)?; let hashes = buffer as *mut HashUint; @@ -772,7 +772,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(table) => { table } } } @@ -811,7 +811,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Heap.oom(e), + Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), Ok(table) => { table } } } @@ -1185,8 +1185,8 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Heap.dealloc(self.hashes.ptr() as *mut u8, - Layout::from_size_align(size, align).unwrap()); + Global.dealloc(self.hashes.ptr() as *mut u8, + Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. } -- cgit 1.4.1-3-g733a5 From 5e5a0c21fc1416e77ae8e4db74b93e3601241e22 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 20:58:50 +0200 Subject: Separate alloc::heap::Alloc trait for stage0 #[global_allocator] --- src/Cargo.lock | 2 - src/liballoc/alloc.rs | 5 -- src/liballoc/heap.rs | 98 ++++++++++++++++++++++++++++++++++++++++ src/liballoc/lib.rs | 6 +++ src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 2 +- src/liballoc_system/Cargo.toml | 1 - src/liballoc_system/lib.rs | 8 ++-- 8 files changed, 109 insertions(+), 14 deletions(-) create mode 100644 src/liballoc/heap.rs (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index 6e7c4b67acf..f573abadc31 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc 0.0.0", "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", @@ -32,7 +31,6 @@ dependencies = [ name = "alloc_system" version = "0.0.0" dependencies = [ - "alloc 0.0.0", "compiler_builtins 0.0.0", "core 0.0.0", "dlmalloc 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 12ee7701903..00a8b2c0e25 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -22,11 +22,6 @@ use core::usize; #[doc(inline)] pub use core::alloc::*; -#[doc(hidden)] -pub mod __core { - pub use core::*; -} - extern "Rust" { #[allocator] #[rustc_allocator_nounwind] diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs new file mode 100644 index 00000000000..a44ff04bd1b --- /dev/null +++ b/src/liballoc/heap.rs @@ -0,0 +1,98 @@ +// Copyright 2014-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub use alloc::{Excess, Layout, AllocErr, CannotReallocInPlace}; +use core::alloc::Alloc as CoreAlloc; + +#[doc(hidden)] +pub mod __core { + pub use core::*; +} + +/// Compatibility with older versions of #[global_allocator] during bootstrap +pub unsafe trait Alloc { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + fn oom(&mut self, err: AllocErr) -> !; + fn usable_size(&self, layout: &Layout) -> (usize, usize); + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result; + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result; + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace>; + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace>; +} + +#[allow(deprecated)] +unsafe impl Alloc for T where T: CoreAlloc { + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::alloc(self, layout) + } + + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + CoreAlloc::dealloc(self, ptr, layout) + } + + fn oom(&mut self, err: AllocErr) -> ! { + CoreAlloc::oom(self, err) + } + + fn usable_size(&self, layout: &Layout) -> (usize, usize) { + CoreAlloc::usable_size(self, layout) + } + + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::realloc(self, ptr, layout, new_layout) + } + + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + CoreAlloc::alloc_zeroed(self, layout) + } + + unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { + CoreAlloc::alloc_excess(self, layout) + } + + unsafe fn realloc_excess(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result { + CoreAlloc::realloc_excess(self, ptr, layout, new_layout) + } + + unsafe fn grow_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + CoreAlloc::grow_in_place(self, ptr, layout, new_layout) + } + + unsafe fn shrink_in_place(&mut self, + ptr: *mut u8, + layout: Layout, + new_layout: Layout) -> Result<(), CannotReallocInPlace> { + CoreAlloc::shrink_in_place(self, ptr, layout, new_layout) + } +} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 066698a71df..f6598fe5e89 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -153,10 +153,16 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] /// Use the `alloc` module instead. +#[cfg(not(stage0))] pub mod heap { pub use alloc::*; } +#[unstable(feature = "allocator_api", issue = "32838")] +#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] +#[cfg(stage0)] +pub mod heap; + // Primitive types using the heaps above // Need to conditionally define the mod from `boxed.rs` to avoid diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index fd4a4553046..02435170374 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc = { path = "../liballoc" } alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index df7e3f61f5f..616181d99bc 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -32,7 +32,7 @@ pub use contents::*; mod contents { use core::ptr; - use core::heap::{Alloc, AllocErr, Layout}; + use core::alloc::{Alloc, AllocErr, Layout}; use alloc_system::System; use libc::{c_int, c_void, size_t}; diff --git a/src/liballoc_system/Cargo.toml b/src/liballoc_system/Cargo.toml index 936e20a32e1..c34e2f203a8 100644 --- a/src/liballoc_system/Cargo.toml +++ b/src/liballoc_system/Cargo.toml @@ -10,7 +10,6 @@ test = false doc = false [dependencies] -alloc = { path = "../liballoc" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index cdcb732f635..2d5adca7fcb 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; +use core::alloc::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; @@ -121,7 +121,7 @@ mod platform { use MIN_ALIGN; use System; - use core::heap::{Alloc, AllocErr, Layout}; + use core::alloc::{Alloc, AllocErr, Layout}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { @@ -283,7 +283,7 @@ mod platform { use MIN_ALIGN; use System; - use core::heap::{Alloc, AllocErr, Layout, CannotReallocInPlace}; + use core::alloc::{Alloc, AllocErr, Layout, CannotReallocInPlace}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -495,7 +495,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::heap::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; + use core::alloc::{Alloc, AllocErr, Layout, Excess, CannotReallocInPlace}; use System; use self::dlmalloc::GlobalDlmalloc; -- cgit 1.4.1-3-g733a5 From ba7081a033de4981ccad1e1525c8b5191ce02208 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 15:41:09 +0200 Subject: Make AllocErr a zero-size unit struct --- src/liballoc/alloc.rs | 32 ++++++++++++------------ src/liballoc/raw_vec.rs | 2 +- src/liballoc_jemalloc/lib.rs | 25 +++---------------- src/liballoc_system/lib.rs | 24 +++++++----------- src/libcore/alloc.rs | 58 ++++++-------------------------------------- src/libstd/alloc.rs | 43 ++++++++++---------------------- src/libstd/error.rs | 2 +- 7 files changed, 51 insertions(+), 135 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 00a8b2c0e25..b975ff6be58 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,7 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::mem::{self, ManuallyDrop}; +use core::mem; use core::usize; #[doc(inline)] @@ -86,12 +86,12 @@ pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_alloc(layout.size(), layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(ptr) } @@ -129,15 +129,15 @@ unsafe impl Alloc for Global { new_layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size(), new_layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { mem::forget(err); Ok(ptr) @@ -146,12 +146,12 @@ unsafe impl Alloc for Global { #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(ptr) } @@ -159,14 +159,14 @@ unsafe impl Alloc for Global { #[inline] unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let mut size = 0; let ptr = __rust_alloc_excess(layout.size(), layout.align(), &mut size, - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(Excess(ptr, size)) } @@ -177,7 +177,7 @@ unsafe impl Alloc for Global { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let mut err = ManuallyDrop::new(mem::uninitialized::()); + let mut err = AllocErr; let mut size = 0; let ptr = __rust_realloc_excess(ptr, layout.size(), @@ -185,9 +185,9 @@ unsafe impl Alloc for Global { new_layout.size(), new_layout.align(), &mut size, - &mut *err as *mut AllocErr as *mut u8); + &mut err as *mut AllocErr as *mut u8); if ptr.is_null() { - Err(ManuallyDrop::into_inner(err)) + Err(AllocErr) } else { Ok(Excess(ptr, size)) } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 51f39dc6cc7..caedb971ddc 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -760,7 +760,7 @@ mod tests { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { let size = layout.size(); if size > self.fuel { - return Err(AllocErr::Unsupported { details: "fuel exhausted" }); + return Err(AllocErr); } match Global.alloc(layout) { ok @ Ok(_) => { self.fuel -= size; ok } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 616181d99bc..59a7e87e1ec 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -30,8 +30,6 @@ extern crate libc; pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::ptr; - use core::alloc::{Alloc, AllocErr, Layout}; use alloc_system::System; use libc::{c_int, c_void, size_t}; @@ -106,14 +104,9 @@ mod contents { #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let flags = align_to_flags(align, size); let ptr = mallocx(size as size_t, flags) as *mut u8; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(size, align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } @@ -155,20 +148,13 @@ mod contents { old_align: usize, new_size: usize, new_align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { if new_align != old_align { - ptr::write(err as *mut AllocErr, - AllocErr::Unsupported { details: "can't change alignments" }); return 0 as *mut u8 } let flags = align_to_flags(new_align, new_size); let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(new_size, new_align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } @@ -176,18 +162,13 @@ mod contents { #[rustc_std_internal_symbol] pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let ptr = if align <= MIN_ALIGN && align <= size { calloc(size as size_t, 1) as *mut u8 } else { let flags = align_to_flags(align, size) | MALLOCX_ZERO; mallocx(size as size_t, flags) as *mut u8 }; - if ptr.is_null() { - let layout = Layout::from_size_align_unchecked(size, align); - ptr::write(err as *mut AllocErr, - AllocErr::Exhausted { request: layout }); - } ptr } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 6f928287ef2..5dca05cf085 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -133,9 +133,7 @@ mod platform { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - return Err(AllocErr::Unsupported { - details: "requested alignment too large" - }) + return Err(AllocErr) } } aligned_malloc(&layout) @@ -143,7 +141,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } } @@ -156,7 +154,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } } else { let ret = self.alloc(layout.clone()); @@ -178,9 +176,7 @@ mod platform { old_layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { if old_layout.align() != new_layout.align() { - return Err(AllocErr::Unsupported { - details: "cannot change alignment on `realloc`", - }) + return Err(AllocErr) } if new_layout.align() <= MIN_ALIGN && new_layout.align() <= new_layout.size(){ @@ -188,7 +184,7 @@ mod platform { if !ptr.is_null() { Ok(ptr as *mut u8) } else { - Err(AllocErr::Exhausted { request: new_layout }) + Err(AllocErr) } } else { let res = self.alloc(new_layout.clone()); @@ -342,7 +338,7 @@ mod platform { } }; if ptr.is_null() { - Err(AllocErr::Exhausted { request: layout }) + Err(AllocErr) } else { Ok(ptr as *mut u8) } @@ -382,9 +378,7 @@ mod platform { old_layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { if old_layout.align() != new_layout.align() { - return Err(AllocErr::Unsupported { - details: "cannot change alignment on `realloc`", - }) + return Err(AllocErr) } if new_layout.align() <= MIN_ALIGN { @@ -395,7 +389,7 @@ mod platform { if !ptr.is_null() { Ok(ptr as *mut u8) } else { - Err(AllocErr::Exhausted { request: new_layout }) + Err(AllocErr) } } else { let res = self.alloc(new_layout.clone()); @@ -505,7 +499,7 @@ mod platform { if !ptr.is_null() { Ok(ptr) } else { - Err(AllocErr::Unsupported { details: "" }) + Err(AllocErr) } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 5c51bb2b51b..b6626ff9f26 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -320,50 +320,12 @@ impl Layout { /// something wrong when combining the given input arguments with this /// allocator. #[derive(Clone, PartialEq, Eq, Debug)] -pub enum AllocErr { - /// Error due to hitting some resource limit or otherwise running - /// out of memory. This condition strongly implies that *some* - /// series of deallocations would allow a subsequent reissuing of - /// the original allocation request to succeed. - Exhausted { request: Layout }, - - /// Error due to allocator being fundamentally incapable of - /// satisfying the original request. This condition implies that - /// such an allocation request will never succeed on the given - /// allocator, regardless of environment, memory pressure, or - /// other contextual conditions. - /// - /// For example, an allocator that does not support requests for - /// large memory blocks might return this error variant. - Unsupported { details: &'static str }, -} - -impl AllocErr { - #[inline] - pub fn invalid_input(details: &'static str) -> Self { - AllocErr::Unsupported { details: details } - } - #[inline] - pub fn is_memory_exhausted(&self) -> bool { - if let AllocErr::Exhausted { .. } = *self { true } else { false } - } - #[inline] - pub fn is_request_unsupported(&self) -> bool { - if let AllocErr::Unsupported { .. } = *self { true } else { false } - } - #[inline] - pub fn description(&self) -> &str { - match *self { - AllocErr::Exhausted { .. } => "allocator memory exhausted", - AllocErr::Unsupported { .. } => "unsupported allocator request", - } - } -} +pub struct AllocErr; // (we need this for downstream impl of trait Error) impl fmt::Display for AllocErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "{}", self.description()) + f.write_str("memory allocation failed") } } @@ -592,12 +554,8 @@ pub unsafe trait Alloc { /// aborting. /// /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request (signaled by an error such as - /// `AllocErr::Exhausted`), and wish to abandon computation rather - /// than attempt to recover locally. Such clients should pass the - /// signaling error value back into `oom`, where the allocator - /// may incorporate that error value into its diagnostic report - /// before aborting. + /// unsatisfied allocation request, and wish to abandon + /// computation rather than attempt to recover locally. /// /// Implementations of the `oom` method are discouraged from /// infinitely regressing in nested calls to `oom`. In @@ -963,7 +921,7 @@ pub unsafe trait Alloc { if k.size() > 0 { unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } } else { - Err(AllocErr::invalid_input("zero-sized type invalid for alloc_one")) + Err(AllocErr) } } @@ -1036,7 +994,7 @@ pub unsafe trait Alloc { }) } } - _ => Err(AllocErr::invalid_input("invalid layout for alloc_array")), + _ => Err(AllocErr), } } @@ -1084,7 +1042,7 @@ pub unsafe trait Alloc { .map(|p| NonNull::new_unchecked(p as *mut T)) } _ => { - Err(AllocErr::invalid_input("invalid layout for realloc_array")) + Err(AllocErr) } } } @@ -1118,7 +1076,7 @@ pub unsafe trait Alloc { Ok(self.dealloc(raw_ptr, k.clone())) } _ => { - Err(AllocErr::invalid_input("invalid layout for dealloc_array")) + Err(AllocErr) } } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index eb0c960732d..533ad3ad473 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,9 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, Alloc, AllocErr}; - use ptr; - + use super::{System, Layout, Alloc, AllocErr, CannotReallocInPlace}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -34,14 +32,11 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc(layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -76,15 +71,12 @@ pub mod __default_lib_allocator { old_align: usize, new_size: usize, new_align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, old_align); let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.realloc(ptr, old_layout, new_layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -92,14 +84,11 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc_zeroed(layout) { Ok(p) => p, - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -108,17 +97,14 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_alloc_excess(size: usize, align: usize, excess: *mut usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); match System.alloc_excess(layout) { Ok(p) => { *excess = p.1; p.0 } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -130,7 +116,7 @@ pub mod __default_lib_allocator { new_size: usize, new_align: usize, excess: *mut usize, - err: *mut u8) -> *mut u8 { + _err: *mut u8) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, old_align); let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.realloc_excess(ptr, old_layout, new_layout) { @@ -138,10 +124,7 @@ pub mod __default_lib_allocator { *excess = p.1; p.0 } - Err(e) => { - ptr::write(err as *mut AllocErr, e); - 0 as *mut u8 - } + Err(AllocErr) => 0 as *mut u8, } } @@ -156,7 +139,7 @@ pub mod __default_lib_allocator { let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.grow_in_place(ptr, old_layout, new_layout) { Ok(()) => 1, - Err(_) => 0, + Err(CannotReallocInPlace) => 0, } } @@ -171,7 +154,7 @@ pub mod __default_lib_allocator { let new_layout = Layout::from_size_align_unchecked(new_size, new_align); match System.shrink_in_place(ptr, old_layout, new_layout) { Ok(()) => 1, - Err(_) => 0, + Err(CannotReallocInPlace) => 0, } } } diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 4edb897350e..ec55a3c021a 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -243,7 +243,7 @@ impl Error for ! { issue = "32838")] impl Error for AllocErr { fn description(&self) -> &str { - AllocErr::description(self) + "memory allocation failed" } } -- cgit 1.4.1-3-g733a5 From 86753ce1cc520bfe50ae89f09ec47f313ce900eb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 17:12:57 +0200 Subject: Use the GlobalAlloc trait for #[global_allocator] --- src/Cargo.lock | 1 - .../src/language-features/global-allocator.md | 8 +- src/liballoc/alloc.rs | 188 +++----------- src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 110 +------- src/librustc_allocator/expand.rs | 283 +++------------------ src/librustc_allocator/lib.rs | 39 +-- src/librustc_trans/allocator.rs | 28 +- src/libstd/alloc.rs | 153 ++++------- src/llvm | 2 +- src/rustllvm/llvm-rebuild-trigger | 2 +- .../compile-fail/allocator/not-an-allocator.rs | 14 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 8 +- src/test/run-pass/allocator/auxiliary/custom.rs | 8 +- src/test/run-pass/allocator/custom.rs | 12 +- src/test/run-pass/allocator/xcrate-use.rs | 6 +- src/test/run-pass/allocator/xcrate-use2.rs | 12 +- 17 files changed, 168 insertions(+), 707 deletions(-) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index e5297d1482e..2e969f4ec2b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index b3e6925b666..6ce12ba684d 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,16 +29,16 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::heap::{Alloc, System, Layout, AllocErr}; +use std::alloc::{GlobalAlloc, System, Layout, Void}; struct MyAllocator; -unsafe impl<'a> Alloc for &'a MyAllocator { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for MyAllocator { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index b975ff6be58..73bc78eb8a2 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,26 +16,19 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::mem; use core::usize; #[doc(inline)] pub use core::alloc::*; +#[cfg(stage0)] 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, @@ -44,31 +37,22 @@ extern "Rust" { err: *mut u8) -> *mut u8; #[rustc_allocator_nounwind] fn __rust_alloc_zeroed(size: usize, align: usize, err: *mut u8) -> *mut u8; +} + +#[cfg(not(stage0))] +extern "Rust" { + #[allocator] #[rustc_allocator_nounwind] - fn __rust_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8; + fn __rust_alloc(size: usize, align: usize) -> *mut u8; #[rustc_allocator_nounwind] - fn __rust_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8; + fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] - fn __rust_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8; + fn __rust_realloc(ptr: *mut u8, + old_size: usize, + align: usize, + new_size: usize) -> *mut u8; #[rustc_allocator_nounwind] - fn __rust_shrink_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8; + fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; } #[derive(Copy, Clone, Default, Debug)] @@ -86,22 +70,15 @@ pub const Heap: Global = Global; unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_alloc(layout.size(), - layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) - } else { - Ok(ptr) - } - } + #[cfg(not(stage0))] + let ptr = __rust_alloc(layout.size(), layout.align()); + #[cfg(stage0)] + let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - #[inline] - #[cold] - fn oom(&mut self, err: AllocErr) -> ! { - unsafe { - __rust_oom(&err as *const AllocErr as *const u8) + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr) } } @@ -110,18 +87,6 @@ unsafe impl Alloc for Global { __rust_dealloc(ptr, layout.size(), layout.align()) } - #[inline] - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - let mut min = 0; - let mut max = 0; - unsafe { - __rust_usable_size(layout as *const Layout as *const u8, - &mut min, - &mut max); - } - (min, max) - } - #[inline] unsafe fn realloc(&mut self, ptr: *mut u8, @@ -129,107 +94,34 @@ unsafe impl Alloc for Global { new_layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_realloc(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) + if layout.align() == new_layout.align() { + #[cfg(not(stage0))] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size()); + #[cfg(stage0)] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + new_layout.size(), new_layout.align(), &mut 0); + + if !ptr.is_null() { + Ok(ptr) + } else { + Err(AllocErr) + } } else { - mem::forget(err); - Ok(ptr) + Err(AllocErr) } } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - let mut err = AllocErr; - let ptr = __rust_alloc_zeroed(layout.size(), - layout.align(), - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) - } else { - Ok(ptr) - } - } + #[cfg(not(stage0))] + let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); + #[cfg(stage0)] + let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - #[inline] - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - let mut err = AllocErr; - let mut size = 0; - let ptr = __rust_alloc_excess(layout.size(), - layout.align(), - &mut size, - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { - Err(AllocErr) + if !ptr.is_null() { + Ok(ptr) } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let mut err = AllocErr; - let mut size = 0; - let ptr = __rust_realloc_excess(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align(), - &mut size, - &mut err as *mut AllocErr as *mut u8); - if ptr.is_null() { Err(AllocErr) - } else { - Ok(Excess(ptr, size)) - } - } - - #[inline] - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) - -> Result<(), CannotReallocInPlace> - { - debug_assert!(new_layout.size() >= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_grow_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) - } - } - - #[inline] - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - debug_assert!(new_layout.size() <= layout.size()); - debug_assert!(new_layout.align() == layout.align()); - let ret = __rust_shrink_in_place(ptr, - layout.size(), - layout.align(), - new_layout.size(), - new_layout.align()); - if ret != 0 { - Ok(()) - } else { - Err(CannotReallocInPlace) } } } diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 02435170374..7986d5dd2eb 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 59a7e87e1ec..661d7ab78da 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,7 +14,6 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -23,15 +22,12 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] -extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::alloc::{Alloc, AllocErr, Layout}; - use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -50,18 +46,10 @@ mod contents { target_os = "dragonfly", target_os = "windows", target_env = "musl"), link_name = "je_rallocx")] fn rallocx(ptr: *mut c_void, size: size_t, flags: c_int) -> *mut c_void; - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", - target_os = "dragonfly", target_os = "windows", target_env = "musl"), - link_name = "je_xallocx")] - fn xallocx(ptr: *mut c_void, size: size_t, extra: size_t, flags: c_int) -> size_t; #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", target_os = "dragonfly", target_os = "windows", target_env = "musl"), link_name = "je_sdallocx")] fn sdallocx(ptr: *mut c_void, size: size_t, flags: c_int); - #[cfg_attr(any(target_os = "macos", target_os = "android", target_os = "ios", - target_os = "dragonfly", target_os = "windows", target_env = "musl"), - link_name = "je_nallocx")] - fn nallocx(size: size_t, flags: c_int) -> size_t; } const MALLOCX_ZERO: c_int = 0x40; @@ -102,20 +90,12 @@ mod contents { #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rde_alloc(size: usize, align: usize) -> *mut u8 { let flags = align_to_flags(align, size); let ptr = mallocx(size as size_t, flags) as *mut u8; ptr } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_oom(err: *const u8) -> ! { - System.oom((*(err as *const AllocErr)).clone()) - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, @@ -125,44 +105,20 @@ mod contents { sdallocx(ptr as *mut c_void, size, flags); } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize) { - let layout = &*(layout as *const Layout); - let flags = align_to_flags(layout.align(), layout.size()); - let size = nallocx(layout.size(), flags) as usize; - *min = layout.size(); - if size > 0 { - *max = size; - } else { - *max = layout.size(); - } - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_realloc(ptr: *mut u8, _old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - _err: *mut u8) -> *mut u8 { - if new_align != old_align { - return 0 as *mut u8 - } - - let flags = align_to_flags(new_align, new_size); + align: usize, + new_size: usize) -> *mut u8 { + let flags = align_to_flags(align, new_size); let ptr = rallocx(ptr as *mut c_void, new_size, flags) as *mut u8; ptr } #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc_zeroed(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rde_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let ptr = if align <= MIN_ALIGN && align <= size { calloc(size as size_t, 1) as *mut u8 } else { @@ -171,60 +127,4 @@ mod contents { }; ptr } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let p = __rde_alloc(size, align, err); - if !p.is_null() { - let flags = align_to_flags(align, size); - *excess = nallocx(size, flags) as usize; - } - return p - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - err: *mut u8) -> *mut u8 { - let p = __rde_realloc(ptr, old_size, old_align, new_size, new_align, err); - if !p.is_null() { - let flags = align_to_flags(new_align, new_size); - *excess = nallocx(new_size, flags) as usize; - } - p - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - __rde_shrink_in_place(ptr, old_size, old_align, new_size, new_align) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_shrink_in_place(ptr: *mut u8, - _old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - if old_align == new_align { - let flags = align_to_flags(new_align, new_size); - (xallocx(ptr as *mut c_void, new_size, 0, flags) == new_size) as u8 - } else { - 0 - } - } } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ee38cca7828..ce41fe1f3bc 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -11,7 +11,7 @@ use rustc::middle::allocator::AllocatorKind; use rustc_errors; use syntax::abi::Abi; -use syntax::ast::{Crate, Attribute, LitKind, StrStyle, ExprKind}; +use syntax::ast::{Crate, Attribute, LitKind, StrStyle}; use syntax::ast::{Unsafety, Constness, Generics, Mutability, Ty, Mac, Arg}; use syntax::ast::{self, Ident, Item, ItemKind, TyKind, VisibilityKind, Expr}; use syntax::attr; @@ -88,7 +88,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { span, kind: AllocatorKind::Global, global: item.ident, - alloc: Ident::from_str("alloc"), + core: Ident::from_str("core"), cx: ExtCtxt::new(self.sess, ecfg, self.resolver), }; let super_path = f.cx.path(f.span, vec![ @@ -96,7 +96,7 @@ impl<'a> Folder for ExpandAllocatorDirectives<'a> { f.global, ]); let mut items = vec![ - f.cx.item_extern_crate(f.span, f.alloc), + f.cx.item_extern_crate(f.span, f.core), f.cx.item_use_simple( f.span, respan(f.span.shrink_to_lo(), VisibilityKind::Inherited), @@ -126,7 +126,7 @@ struct AllocFnFactory<'a> { span: Span, kind: AllocatorKind, global: Ident, - alloc: Ident, + core: Ident, cx: ExtCtxt<'a>, } @@ -143,8 +143,7 @@ impl<'a> AllocFnFactory<'a> { self.arg_ty(ty, &mut abi_args, mk) }).collect(); let result = self.call_allocator(method.name, args); - let (output_ty, output_expr) = - self.ret_ty(&method.output, &mut abi_args, mk, result); + let (output_ty, output_expr) = self.ret_ty(&method.output, result); let kind = ItemKind::Fn(self.cx.fn_decl(abi_args, ast::FunctionRetTy::Ty(output_ty)), Unsafety::Unsafe, dummy_spanned(Constness::NotConst), @@ -159,16 +158,15 @@ impl<'a> AllocFnFactory<'a> { fn call_allocator(&self, method: &str, mut args: Vec>) -> P { let method = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("Alloc"), + self.core, + Ident::from_str("alloc"), + Ident::from_str("GlobalAlloc"), Ident::from_str(method), ]); let method = self.cx.expr_path(method); let allocator = self.cx.path_ident(self.span, self.global); let allocator = self.cx.expr_path(allocator); let allocator = self.cx.expr_addr_of(self.span, allocator); - let allocator = self.cx.expr_mut_addr_of(self.span, allocator); args.insert(0, allocator); self.cx.expr_call(self.span, method, args) @@ -205,8 +203,8 @@ impl<'a> AllocFnFactory<'a> { args.push(self.cx.arg(self.span, align, ty_usize)); let layout_new = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), + self.core, + Ident::from_str("alloc"), Ident::from_str("Layout"), Ident::from_str("from_size_align_unchecked"), ]); @@ -219,286 +217,67 @@ impl<'a> AllocFnFactory<'a> { layout } - AllocatorTy::LayoutRef => { - let ident = ident(); - args.push(self.cx.arg(self.span, ident, self.ptr_u8())); - - // Convert our `arg: *const u8` via: - // - // &*(arg as *const Layout) - let expr = self.cx.expr_ident(self.span, ident); - let expr = self.cx.expr_cast(self.span, expr, self.layout_ptr()); - let expr = self.cx.expr_deref(self.span, expr); - self.cx.expr_addr_of(self.span, expr) - } - - AllocatorTy::AllocErr => { - // We're creating: - // - // (*(arg as *const AllocErr)).clone() + AllocatorTy::Ptr => { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); - let expr = self.cx.expr_ident(self.span, ident); - let expr = self.cx.expr_cast(self.span, expr, self.alloc_err_ptr()); - let expr = self.cx.expr_deref(self.span, expr); - self.cx.expr_method_call( - self.span, - expr, - Ident::from_str("clone"), - Vec::new() - ) + let arg = self.cx.expr_ident(self.span, ident); + self.cx.expr_cast(self.span, arg, self.ptr_void()) } - AllocatorTy::Ptr => { + AllocatorTy::Usize => { let ident = ident(); - args.push(self.cx.arg(self.span, ident, self.ptr_u8())); + args.push(self.cx.arg(self.span, ident, self.usize())); self.cx.expr_ident(self.span, ident) } AllocatorTy::ResultPtr | - AllocatorTy::ResultExcess | - AllocatorTy::ResultUnit | - AllocatorTy::Bang | - AllocatorTy::UsizePair | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } } } - fn ret_ty(&self, - ty: &AllocatorTy, - args: &mut Vec, - ident: &mut FnMut() -> Ident, - expr: P) -> (P, P) - { + fn ret_ty(&self, ty: &AllocatorTy, expr: P) -> (P, P) { match *ty { - AllocatorTy::UsizePair => { - // We're creating: - // - // let arg = #expr; - // *min = arg.0; - // *max = arg.1; - - let min = ident(); - let max = ident(); - - args.push(self.cx.arg(self.span, min, self.ptr_usize())); - args.push(self.cx.arg(self.span, max, self.ptr_usize())); - - let ident = ident(); - let stmt = self.cx.stmt_let(self.span, false, ident, expr); - let min = self.cx.expr_ident(self.span, min); - let max = self.cx.expr_ident(self.span, max); - let layout = self.cx.expr_ident(self.span, ident); - let assign_min = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, min), - self.cx.expr_tup_field_access(self.span, layout.clone(), 0), - )); - let assign_min = self.cx.stmt_semi(assign_min); - let assign_max = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, max), - self.cx.expr_tup_field_access(self.span, layout.clone(), 1), - )); - let assign_max = self.cx.stmt_semi(assign_max); - - let stmts = vec![stmt, assign_min, assign_max]; - let block = self.cx.block(self.span, stmts); - let ty_unit = self.cx.ty(self.span, TyKind::Tup(Vec::new())); - (ty_unit, self.cx.expr_block(block)) - } - - AllocatorTy::ResultExcess => { - // We're creating: - // - // match #expr { - // Ok(ptr) => { - // *excess = ptr.1; - // ptr.0 - // } - // Err(e) => { - // ptr::write(err_ptr, e); - // 0 as *mut u8 - // } - // } - - let excess_ptr = ident(); - args.push(self.cx.arg(self.span, excess_ptr, self.ptr_usize())); - let excess_ptr = self.cx.expr_ident(self.span, excess_ptr); - - let err_ptr = ident(); - args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); - let err_ptr = self.cx.expr_ident(self.span, err_ptr); - let err_ptr = self.cx.expr_cast(self.span, - err_ptr, - self.alloc_err_ptr()); - - let name = ident(); - let ok_expr = { - let ptr = self.cx.expr_ident(self.span, name); - let write = self.cx.expr(self.span, ExprKind::Assign( - self.cx.expr_deref(self.span, excess_ptr), - self.cx.expr_tup_field_access(self.span, ptr.clone(), 1), - )); - let write = self.cx.stmt_semi(write); - let ret = self.cx.expr_tup_field_access(self.span, - ptr.clone(), - 0); - let ret = self.cx.stmt_expr(ret); - let block = self.cx.block(self.span, vec![write, ret]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); - let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); - let ok = self.cx.arm(self.span, vec![ok], ok_expr); - - let name = ident(); - let err_expr = { - let err = self.cx.expr_ident(self.span, name); - let write = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("__core"), - Ident::from_str("ptr"), - Ident::from_str("write"), - ]); - let write = self.cx.expr_path(write); - let write = self.cx.expr_call(self.span, write, - vec![err_ptr, err]); - let write = self.cx.stmt_semi(write); - let null = self.cx.expr_usize(self.span, 0); - let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); - let null = self.cx.stmt_expr(null); - let block = self.cx.block(self.span, vec![write, null]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let err = self.cx.path_ident(self.span, Ident::from_str("Err")); - let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); - let err = self.cx.arm(self.span, vec![err], err_expr); - - let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); - (self.ptr_u8(), expr) - } - AllocatorTy::ResultPtr => { // We're creating: // - // match #expr { - // Ok(ptr) => ptr, - // Err(e) => { - // ptr::write(err_ptr, e); - // 0 as *mut u8 - // } - // } - - let err_ptr = ident(); - args.push(self.cx.arg(self.span, err_ptr, self.ptr_u8())); - let err_ptr = self.cx.expr_ident(self.span, err_ptr); - let err_ptr = self.cx.expr_cast(self.span, - err_ptr, - self.alloc_err_ptr()); + // #expr as *mut u8 - let name = ident(); - let ok_expr = self.cx.expr_ident(self.span, name); - let pat = self.cx.pat_ident(self.span, name); - let ok = self.cx.path_ident(self.span, Ident::from_str("Ok")); - let ok = self.cx.pat_tuple_struct(self.span, ok, vec![pat]); - let ok = self.cx.arm(self.span, vec![ok], ok_expr); - - let name = ident(); - let err_expr = { - let err = self.cx.expr_ident(self.span, name); - let write = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("__core"), - Ident::from_str("ptr"), - Ident::from_str("write"), - ]); - let write = self.cx.expr_path(write); - let write = self.cx.expr_call(self.span, write, - vec![err_ptr, err]); - let write = self.cx.stmt_semi(write); - let null = self.cx.expr_usize(self.span, 0); - let null = self.cx.expr_cast(self.span, null, self.ptr_u8()); - let null = self.cx.stmt_expr(null); - let block = self.cx.block(self.span, vec![write, null]); - self.cx.expr_block(block) - }; - let pat = self.cx.pat_ident(self.span, name); - let err = self.cx.path_ident(self.span, Ident::from_str("Err")); - let err = self.cx.pat_tuple_struct(self.span, err, vec![pat]); - let err = self.cx.arm(self.span, vec![err], err_expr); - - let expr = self.cx.expr_match(self.span, expr, vec![ok, err]); + let expr = self.cx.expr_cast(self.span, expr, self.ptr_u8()); (self.ptr_u8(), expr) } - AllocatorTy::ResultUnit => { - // We're creating: - // - // #expr.is_ok() as u8 - - let cast = self.cx.expr_method_call( - self.span, - expr, - Ident::from_str("is_ok"), - Vec::new() - ); - let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); - let u8 = self.cx.ty_path(u8); - let cast = self.cx.expr_cast(self.span, cast, u8.clone()); - (u8, cast) - } - - AllocatorTy::Bang => { - (self.cx.ty(self.span, TyKind::Never), expr) - } - AllocatorTy::Unit => { (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr) } - AllocatorTy::AllocErr | AllocatorTy::Layout | - AllocatorTy::LayoutRef | + AllocatorTy::Usize | AllocatorTy::Ptr => { panic!("can't convert AllocatorTy to an output") } } } + fn usize(&self) -> P { + let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); + self.cx.ty_path(usize) + } + fn ptr_u8(&self) -> P { let u8 = self.cx.path_ident(self.span, Ident::from_str("u8")); let ty_u8 = self.cx.ty_path(u8); self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - fn ptr_usize(&self) -> P { - let usize = self.cx.path_ident(self.span, Ident::from_str("usize")); - let ty_usize = self.cx.ty_path(usize); - self.cx.ty_ptr(self.span, ty_usize, Mutability::Mutable) - } - - fn layout_ptr(&self) -> P { - let layout = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("Layout"), - ]); - let layout = self.cx.ty_path(layout); - self.cx.ty_ptr(self.span, layout, Mutability::Mutable) - } - - fn alloc_err_ptr(&self) -> P { - let err = self.cx.path(self.span, vec![ - self.alloc, - Ident::from_str("heap"), - Ident::from_str("AllocErr"), + fn ptr_void(&self) -> P { + let void = self.cx.path(self.span, vec![ + self.core, + Ident::from_str("alloc"), + Ident::from_str("Void"), ]); - let err = self.cx.ty_path(err); - self.cx.ty_ptr(self.span, err, Mutability::Mutable) + let ty_void = self.cx.ty_path(void); + self.cx.ty_ptr(self.span, ty_void, Mutability::Mutable) } } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 0c7a9a91711..969086815de 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,24 +23,14 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "oom", - inputs: &[AllocatorTy::AllocErr], - output: AllocatorTy::Bang, - }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], output: AllocatorTy::Unit, }, - AllocatorMethod { - name: "usable_size", - inputs: &[AllocatorTy::LayoutRef], - output: AllocatorTy::UsizePair, - }, AllocatorMethod { name: "realloc", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], + inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Usize], output: AllocatorTy::ResultPtr, }, AllocatorMethod { @@ -48,26 +38,6 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "alloc_excess", - inputs: &[AllocatorTy::Layout], - output: AllocatorTy::ResultExcess, - }, - AllocatorMethod { - name: "realloc_excess", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultExcess, - }, - AllocatorMethod { - name: "grow_in_place", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultUnit, - }, - AllocatorMethod { - name: "shrink_in_place", - inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout, AllocatorTy::Layout], - output: AllocatorTy::ResultUnit, - }, ]; pub struct AllocatorMethod { @@ -77,14 +47,9 @@ pub struct AllocatorMethod { } pub enum AllocatorTy { - AllocErr, - Bang, Layout, - LayoutRef, Ptr, - ResultExcess, ResultPtr, - ResultUnit, Unit, - UsizePair, + Usize, } diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs index e1c145b122d..ffebb959ebf 100644 --- a/src/librustc_trans/allocator.rs +++ b/src/librustc_trans/allocator.rs @@ -30,7 +30,6 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) }; let i8 = llvm::LLVMInt8TypeInContext(llcx); let i8p = llvm::LLVMPointerType(i8, 0); - let usizep = llvm::LLVMPointerType(usize, 0); let void = llvm::LLVMVoidTypeInContext(llcx); for method in ALLOCATOR_METHODS { @@ -41,40 +40,19 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) args.push(usize); // size args.push(usize); // align } - AllocatorTy::LayoutRef => args.push(i8p), AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::AllocErr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), - AllocatorTy::Bang | - AllocatorTy::ResultExcess | AllocatorTy::ResultPtr | - AllocatorTy::ResultUnit | - AllocatorTy::UsizePair | AllocatorTy::Unit => panic!("invalid allocator arg"), } } let output = match method.output { - AllocatorTy::UsizePair => { - args.push(usizep); // min - args.push(usizep); // max - None - } - AllocatorTy::Bang => None, - AllocatorTy::ResultExcess => { - args.push(i8p); // excess_ptr - args.push(i8p); // err_ptr - Some(i8p) - } - AllocatorTy::ResultPtr => { - args.push(i8p); // err_ptr - Some(i8p) - } - AllocatorTy::ResultUnit => Some(i8), + AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, - AllocatorTy::AllocErr | AllocatorTy::Layout | - AllocatorTy::LayoutRef | + AllocatorTy::Usize | AllocatorTy::Ptr => panic!("invalid allocator output"), }; let ty = llvm::LLVMFunctionType(output.unwrap_or(void), diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 533ad3ad473..335dc7e0412 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,7 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, Alloc, AllocErr, CannotReallocInPlace}; + use super::{System, Layout, GlobalAlloc, Void}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -30,20 +30,9 @@ pub mod __default_lib_allocator { #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc(layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom(err: *const u8) -> ! { - System.oom((*(err as *const AllocErr)).clone()) + System.alloc(layout) as *mut u8 } #[no_mangle] @@ -51,110 +40,76 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(layout: *const u8, - min: *mut usize, - max: *mut usize) { - let pair = System.usable_size(&*(layout as *const Layout)); - *min = pair.0; - *max = pair.1; + System.dealloc(ptr as *mut Void, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_realloc(ptr: *mut u8, old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - _err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc(ptr, old_layout, new_layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } + align: usize, + new_size: usize) -> *mut u8 { + let old_layout = Layout::from_size_align_unchecked(old_size, align); + System.realloc(ptr as *mut Void, old_layout, new_size) as *mut u8 } #[no_mangle] #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_zeroed(size: usize, - align: usize, - _err: *mut u8) -> *mut u8 { + pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_zeroed(layout) { - Ok(p) => p, - Err(AllocErr) => 0 as *mut u8, - } + System.alloc_zeroed(layout) as *mut u8 } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(size: usize, - align: usize, - excess: *mut usize, - _err: *mut u8) -> *mut u8 { - let layout = Layout::from_size_align_unchecked(size, align); - match System.alloc_excess(layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(AllocErr) => 0 as *mut u8, + #[cfg(stage0)] + pub mod stage0 { + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_usable_size(_layout: *const u8, + _min: *mut usize, + _max: *mut usize) { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize, - excess: *mut usize, - _err: *mut u8) -> *mut u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.realloc_excess(ptr, old_layout, new_layout) { - Ok(p) => { - *excess = p.1; - p.0 - } - Err(AllocErr) => 0 as *mut u8, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_alloc_excess(_size: usize, + _align: usize, + _excess: *mut usize, + _err: *mut u8) -> *mut u8 { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.grow_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(CannotReallocInPlace) => 0, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize, + _excess: *mut usize, + _err: *mut u8) -> *mut u8 { + unimplemented!() } - } - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(ptr: *mut u8, - old_size: usize, - old_align: usize, - new_size: usize, - new_align: usize) -> u8 { - let old_layout = Layout::from_size_align_unchecked(old_size, old_align); - let new_layout = Layout::from_size_align_unchecked(new_size, new_align); - match System.shrink_in_place(ptr, old_layout, new_layout) { - Ok(()) => 1, - Err(CannotReallocInPlace) => 0, + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + unimplemented!() } + + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8, + _old_size: usize, + _old_align: usize, + _new_size: usize, + _new_align: usize) -> u8 { + unimplemented!() + } + } } diff --git a/src/llvm b/src/llvm index 6ceaaa4b017..7243155b1c3 160000 --- a/src/llvm +++ b/src/llvm @@ -1 +1 @@ -Subproject commit 6ceaaa4b0176a200e4bbd347d6a991ab6c776ede +Subproject commit 7243155b1c3da0a980c868a87adebf00e0b33989 diff --git a/src/rustllvm/llvm-rebuild-trigger b/src/rustllvm/llvm-rebuild-trigger index c4c0f1ab6e6..c3fc3e5452c 100644 --- a/src/rustllvm/llvm-rebuild-trigger +++ b/src/rustllvm/llvm-rebuild-trigger @@ -1,4 +1,4 @@ # If this file is modified, then llvm will be (optionally) cleaned and then rebuilt. # The actual contents of this file do not matter, but to trigger a change on the # build bots then the contents should be changed so git updates the mtime. -2018-03-10 +2018-04-05 diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index e4301435063..140cad22f34 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -12,15 +12,9 @@ #[global_allocator] static A: usize = 0; -//~^ the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: -//~| the trait bound `&usize: +//~^ the trait bound `usize: +//~| the trait bound `usize: +//~| the trait bound `usize: +//~| the trait bound `usize: fn main() {} diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 6def5b6f5e1..20b87028fd1 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -11,16 +11,16 @@ #![feature(allocator_api)] #![crate_type = "rlib"] -use std::heap::*; +use std::alloc::*; pub struct A; -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, _: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for A { + unsafe fn alloc(&self, _: Layout) -> *mut Void { loop {} } - unsafe fn dealloc(&mut self, _ptr: *mut u8, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut Void, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 8f4fbcd5ab1..95096efc7ef 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::heap::{Alloc, System, AllocErr, Layout}; +use std::heap::{GlobalAlloc, System, Layout, Void}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl GlobalAlloc for A { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index 22081678fb9..f7b2fd73c87 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,20 +15,20 @@ extern crate helper; -use std::heap::{Heap, Alloc, System, Layout, AllocErr}; +use std::alloc::{self, Global, Alloc, System, Layout, Void}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; -unsafe impl<'a> Alloc for &'a A { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { +unsafe impl alloc::GlobalAlloc for A { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } @@ -45,10 +45,10 @@ fn main() { unsafe { let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(HITS.load(Ordering::SeqCst), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(HITS.load(Ordering::SeqCst), n + 2); let s = String::with_capacity(10); diff --git a/src/test/run-pass/allocator/xcrate-use.rs b/src/test/run-pass/allocator/xcrate-use.rs index 04d2ef466e7..78d604a7108 100644 --- a/src/test/run-pass/allocator/xcrate-use.rs +++ b/src/test/run-pass/allocator/xcrate-use.rs @@ -17,7 +17,7 @@ extern crate custom; extern crate helper; -use std::heap::{Heap, Alloc, System, Layout}; +use std::alloc::{Global, Alloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; #[global_allocator] @@ -28,10 +28,10 @@ fn main() { let n = GLOBAL.0.load(Ordering::SeqCst); let layout = Layout::from_size_align(4, 2).unwrap(); - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), n + 2); let ptr = System.alloc(layout.clone()).unwrap(); diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index 155fb5d6c5d..52eb963efdb 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -19,7 +19,7 @@ extern crate custom; extern crate custom_as_global; extern crate helper; -use std::heap::{Heap, Alloc, System, Layout}; +use std::alloc::{Global, Alloc, GlobalAlloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); @@ -30,25 +30,25 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Heap.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()).unwrap(); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); - Heap.dealloc(ptr, layout.clone()); + Global.dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of the system allocator avoids all globals - let ptr = System.alloc(layout.clone()).unwrap(); + let ptr = System.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 2); System.dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of our personal allocator doesn't affect other instances - let ptr = (&GLOBAL).alloc(layout.clone()).unwrap(); + let ptr = GLOBAL.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 2); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 1); - (&GLOBAL).dealloc(ptr, layout); + GLOBAL.dealloc(ptr, layout); assert_eq!(custom_as_global::get(), n + 2); assert_eq!(GLOBAL.0.load(Ordering::SeqCst), 2); } -- cgit 1.4.1-3-g733a5 From 157ff8cd0562eefdd7aa296395c38a7bc259a4b9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 16:00:04 +0200 Subject: Remove the now-unit-struct AllocErr parameter of oom() --- src/liballoc/alloc.rs | 6 +++--- src/liballoc/arc.rs | 2 +- src/liballoc/heap.rs | 4 ++-- src/liballoc/raw_vec.rs | 12 ++++++------ src/liballoc/rc.rs | 2 +- src/liballoc_system/lib.rs | 12 ++++++------ src/libcore/alloc.rs | 2 +- src/libstd/collections/hash/map.rs | 2 +- src/libstd/collections/hash/table.rs | 4 ++-- src/test/run-pass/allocator-alloc-one.rs | 4 ++-- src/test/run-pass/realloc-16687.rs | 4 ++-- src/test/run-pass/regions-mock-trans.rs | 2 +- 12 files changed, 28 insertions(+), 28 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 73bc78eb8a2..a7b5864016c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -136,8 +136,8 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Global.alloc(layout).unwrap_or_else(|err| { - Global.oom(err) + Global.alloc(layout).unwrap_or_else(|_| { + Global.oom() }) } } @@ -166,7 +166,7 @@ mod tests { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); let end = ptr.offset(layout.size() as isize); let mut i = ptr; diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d63ed24aa4f..f0a325530ba 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -555,7 +555,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index a44ff04bd1b..765fb8458d1 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -52,8 +52,8 @@ unsafe impl Alloc for T where T: CoreAlloc { CoreAlloc::dealloc(self, ptr, layout) } - fn oom(&mut self, err: AllocErr) -> ! { - CoreAlloc::oom(self, err) + fn oom(&mut self, _: AllocErr) -> ! { + CoreAlloc::oom(self) } fn usable_size(&self, layout: &Layout) -> (usize, usize) { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index caedb971ddc..25d759764a5 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -100,7 +100,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr, - Err(err) => a.oom(err), + Err(_) => a.oom(), } }; @@ -316,7 +316,7 @@ impl RawVec { new_layout); match ptr_res { Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), - Err(e) => self.a.oom(e), + Err(_) => self.a.oom(), } } None => { @@ -325,7 +325,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(e) => self.a.oom(e), + Err(_) => self.a.oom(), } } }; @@ -444,7 +444,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(e)) => self.a.oom(e), + Err(AllocErr(_)) => self.a.oom(), Ok(()) => { /* yay */ } } } @@ -554,7 +554,7 @@ impl RawVec { pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(e)) => self.a.oom(e), + Err(AllocErr(_)) => self.a.oom(), Ok(()) => { /* yay */ } } } @@ -669,7 +669,7 @@ impl RawVec { old_layout, new_layout) { Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), - Err(err) => self.a.oom(err), + Err(_) => self.a.oom(), } } self.cap = amount; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index c134b181158..3c0b11bfe74 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|e| Global.oom(e)); + .unwrap_or_else(|_| Global.oom()); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 0480be8d913..5e6b3b5ca11 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -73,8 +73,8 @@ unsafe impl Alloc for System { Alloc::realloc(&mut &*self, ptr, old_layout, new_layout) } - fn oom(&mut self, err: AllocErr) -> ! { - Alloc::oom(&mut &*self, err) + fn oom(&mut self) -> ! { + Alloc::oom(&mut &*self) } #[inline] @@ -242,7 +242,7 @@ mod platform { unsafe impl<'a> Alloc for &'a System { alloc_methods_based_on_global_alloc!(); - fn oom(&mut self, err: AllocErr) -> ! { + fn oom(&mut self) -> ! { use core::fmt::{self, Write}; // Print a message to stderr before aborting to assist with @@ -250,7 +250,7 @@ mod platform { // memory since we are in an OOM situation. Any errors are ignored // while printing since there's nothing we can do about them and we // are about to exit anyways. - drop(writeln!(Stderr, "fatal runtime error: {}", err)); + drop(writeln!(Stderr, "fatal runtime error: {}", AllocErr)); unsafe { ::core::intrinsics::abort(); } @@ -459,11 +459,11 @@ mod platform { } } - fn oom(&mut self, err: AllocErr) -> ! { + fn oom(&mut self) -> ! { use core::fmt::{self, Write}; // Same as with unix we ignore all errors here - drop(writeln!(Stderr, "fatal runtime error: {}", err)); + drop(writeln!(Stderr, "fatal runtime error: {}", AllocErr)); unsafe { ::core::intrinsics::abort(); } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 1c764dab000..1ba4c641065 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -572,7 +572,7 @@ pub unsafe trait Alloc { /// instead they should return an appropriate error from the /// invoked method, and let the client decide whether to invoke /// this `oom` method in response. - fn oom(&mut self, _: AllocErr) -> ! { + fn oom(&mut self) -> ! { unsafe { ::intrinsics::abort() } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index c4ef9e62577..2a00241afc6 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 10bab5df8b5..fcc2eb8fef2 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -772,7 +772,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(table) => { table } } } @@ -811,7 +811,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(e)) => Global.oom(e), + Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), Ok(table) => { table } } } diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index eaa5bc90805..38b8ab50cc7 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -14,8 +14,8 @@ use std::heap::{Heap, Alloc}; fn main() { unsafe { - let ptr = Heap.alloc_one::().unwrap_or_else(|e| { - Heap.oom(e) + let ptr = Heap.alloc_one::().unwrap_or_else(|_| { + Heap.oom() }); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index eddcd5a584a..a562165d21b 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Heap.alloc(layout.clone()).unwrap_or_else(|e| Heap.oom(e)); + let ret = Heap.alloc(layout.clone()).unwrap_or_else(|_| Heap.oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,7 @@ unsafe fn test_triangle() -> bool { } let ret = Heap.realloc(ptr, old.clone(), new.clone()) - .unwrap_or_else(|e| Heap.oom(e)); + .unwrap_or_else(|_| Heap.oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 8f278a315d1..7d34b8fd00f 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -32,7 +32,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Heap.alloc(Layout::new::()) - .unwrap_or_else(|e| Heap.oom(e)); + .unwrap_or_else(|_| Heap.oom()); &*(ptr as *const _) } } -- cgit 1.4.1-3-g733a5 From 93a9ad4897e560ccd5ebc3397afb7d83d990ef42 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Tue, 3 Apr 2018 16:01:29 +0200 Subject: Remove the now-unit-struct AllocErr field inside CollectionAllocErr --- src/liballoc/raw_vec.rs | 4 ++-- src/liballoc/tests/string.rs | 12 ++++++------ src/liballoc/tests/vec.rs | 16 ++++++++-------- src/liballoc/tests/vec_deque.rs | 12 ++++++------ src/libcore/alloc.rs | 6 +++--- src/libstd/collections/hash/map.rs | 4 ++-- src/libstd/collections/hash/table.rs | 4 ++-- 7 files changed, 29 insertions(+), 29 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 25d759764a5..d7c30925f1a 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -444,7 +444,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(_)) => self.a.oom(), + Err(AllocErr) => self.a.oom(), Ok(()) => { /* yay */ } } } @@ -554,7 +554,7 @@ impl RawVec { pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => panic!("capacity overflow"), - Err(AllocErr(_)) => self.a.oom(), + Err(AllocErr) => self.a.oom(), Ok(()) => { /* yay */ } } } diff --git a/src/liballoc/tests/string.rs b/src/liballoc/tests/string.rs index 17d53e4cf3e..befb36baeef 100644 --- a/src/liballoc/tests/string.rs +++ b/src/liballoc/tests/string.rs @@ -575,11 +575,11 @@ fn test_try_reserve() { } else { panic!("usize::MAX should trigger an overflow!") } } else { // Check isize::MAX + 1 is an OOM - if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_CAP + 1) { + if let Err(AllocErr) = empty_string.try_reserve(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } // Check usize::MAX is an OOM - if let Err(AllocErr(_)) = empty_string.try_reserve(MAX_USIZE) { + if let Err(AllocErr) = empty_string.try_reserve(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } @@ -599,7 +599,7 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should always overflow in the add-to-len @@ -637,10 +637,10 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = empty_string.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } else { - if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_CAP + 1) { + if let Err(AllocErr) = empty_string.try_reserve_exact(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } - if let Err(AllocErr(_)) = empty_string.try_reserve_exact(MAX_USIZE) { + if let Err(AllocErr) = empty_string.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } @@ -659,7 +659,7 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { diff --git a/src/liballoc/tests/vec.rs b/src/liballoc/tests/vec.rs index 2895c53009d..e329b45a617 100644 --- a/src/liballoc/tests/vec.rs +++ b/src/liballoc/tests/vec.rs @@ -1016,11 +1016,11 @@ fn test_try_reserve() { } else { panic!("usize::MAX should trigger an overflow!") } } else { // Check isize::MAX + 1 is an OOM - if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP + 1) { + if let Err(AllocErr) = empty_bytes.try_reserve(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } // Check usize::MAX is an OOM - if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_USIZE) { + if let Err(AllocErr) = empty_bytes.try_reserve(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } @@ -1040,7 +1040,7 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should always overflow in the add-to-len @@ -1063,7 +1063,7 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + if let Err(AllocErr) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should fail in the mul-by-size @@ -1103,10 +1103,10 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = empty_bytes.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an overflow!") } } else { - if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { + if let Err(AllocErr) = empty_bytes.try_reserve_exact(MAX_CAP + 1) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } - if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_USIZE) { + if let Err(AllocErr) = empty_bytes.try_reserve_exact(MAX_USIZE) { } else { panic!("usize::MAX should trigger an OOM!") } } } @@ -1125,7 +1125,7 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { @@ -1146,7 +1146,7 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + if let Err(AllocErr) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index 75d3f01f8b6..4d55584e2f4 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -1073,7 +1073,7 @@ fn test_try_reserve() { // VecDeque starts with capacity 7, always adds 1 to the capacity // and also rounds the number to next power of 2 so this is the // furthest we can go without triggering CapacityOverflow - if let Err(AllocErr(_)) = empty_bytes.try_reserve(MAX_CAP) { + if let Err(AllocErr) = empty_bytes.try_reserve(MAX_CAP) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } } @@ -1093,7 +1093,7 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should always overflow in the add-to-len @@ -1116,7 +1116,7 @@ fn test_try_reserve() { if let Err(CapacityOverflow) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { + if let Err(AllocErr) = ten_u32s.try_reserve(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } // Should fail in the mul-by-size @@ -1160,7 +1160,7 @@ fn test_try_reserve_exact() { // VecDeque starts with capacity 7, always adds 1 to the capacity // and also rounds the number to next power of 2 so this is the // furthest we can go without triggering CapacityOverflow - if let Err(AllocErr(_)) = empty_bytes.try_reserve_exact(MAX_CAP) { + if let Err(AllocErr) = empty_bytes.try_reserve_exact(MAX_CAP) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } } @@ -1179,7 +1179,7 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { + if let Err(AllocErr) = ten_bytes.try_reserve_exact(MAX_CAP - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_bytes.try_reserve_exact(MAX_USIZE) { @@ -1200,7 +1200,7 @@ fn test_try_reserve_exact() { if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an overflow!"); } } else { - if let Err(AllocErr(_)) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { + if let Err(AllocErr) = ten_u32s.try_reserve_exact(MAX_CAP/4 - 9) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } if let Err(CapacityOverflow) = ten_u32s.try_reserve_exact(MAX_USIZE - 20) { diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 1ba4c641065..23532c61721 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -356,13 +356,13 @@ pub enum CollectionAllocErr { /// (usually `isize::MAX` bytes). CapacityOverflow, /// Error due to the allocator (see the `AllocErr` type's docs). - AllocErr(AllocErr), + AllocErr, } #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] impl From for CollectionAllocErr { - fn from(err: AllocErr) -> Self { - CollectionAllocErr::AllocErr(err) + fn from(AllocErr: AllocErr) -> Self { + CollectionAllocErr::AllocErr } } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 2a00241afc6..20a4f9b508d 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => Global.oom(), Ok(()) => { /* yay */ } } } @@ -3634,7 +3634,7 @@ mod test_map { if let Err(CapacityOverflow) = empty_bytes.try_reserve(max_no_ovf) { } else { panic!("isize::MAX + 1 should trigger a CapacityOverflow!") } } else { - if let Err(AllocErr(_)) = empty_bytes.try_reserve(max_no_ovf) { + if let Err(AllocErr) = empty_bytes.try_reserve(max_no_ovf) { } else { panic!("isize::MAX + 1 should trigger an OOM!") } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index fcc2eb8fef2..e9bdd4e7d07 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -772,7 +772,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => Global.oom(), Ok(table) => { table } } } @@ -811,7 +811,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr(_)) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => Global.oom(), Ok(table) => { table } } } -- cgit 1.4.1-3-g733a5 From b017742136a5d02b6ba0f2080e97d18a8bfeba4b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 16:03:46 +0200 Subject: Return Result instead of Option in alloc::Layout constructors --- src/liballoc/raw_vec.rs | 4 +-- src/libcore/alloc.rs | 63 +++++++++++++++++++++++------------- src/libstd/collections/hash/table.rs | 2 +- src/libstd/error.rs | 11 ++++++- 4 files changed, 54 insertions(+), 26 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d7c30925f1a..18aaf1de08e 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -422,7 +422,7 @@ impl RawVec { // Nothing we can really do about these checks :( let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?; - let new_layout = Layout::array::(new_cap).ok_or(CapacityOverflow)?; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; alloc_guard(new_layout.size())?; @@ -530,7 +530,7 @@ impl RawVec { } let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).ok_or(CapacityOverflow)?; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size())?; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 23532c61721..0acaf54e0d9 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -94,9 +94,9 @@ impl Layout { /// must not overflow (i.e. the rounded value must be less than /// `usize::MAX`). #[inline] - pub fn from_size_align(size: usize, align: usize) -> Option { + pub fn from_size_align(size: usize, align: usize) -> Result { if !align.is_power_of_two() { - return None; + return Err(LayoutErr { private: () }); } // (power-of-two implies align != 0.) @@ -114,11 +114,11 @@ impl Layout { // Above implies that checking for summation overflow is both // necessary and sufficient. if size > usize::MAX - (align - 1) { - return None; + return Err(LayoutErr { private: () }); } unsafe { - Some(Layout::from_size_align_unchecked(size, align)) + Ok(Layout::from_size_align_unchecked(size, align)) } } @@ -130,7 +130,7 @@ impl Layout { /// a power-of-two nor `size` aligned to `align` fits within the /// address space (i.e. the `Layout::from_size_align` preconditions). #[inline] - pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Layout { + pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { Layout { size: size, align: align } } @@ -229,15 +229,17 @@ impl Layout { /// /// On arithmetic overflow, returns `None`. #[inline] - pub fn repeat(&self, n: usize) -> Option<(Self, usize)> { - let padded_size = self.size.checked_add(self.padding_needed_for(self.align))?; - let alloc_size = padded_size.checked_mul(n)?; + pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { + let padded_size = self.size.checked_add(self.padding_needed_for(self.align)) + .ok_or(LayoutErr { private: () })?; + let alloc_size = padded_size.checked_mul(n) + .ok_or(LayoutErr { private: () })?; // We can assume that `self.align` is a power-of-two. // Furthermore, `alloc_size` has already been rounded up // to a multiple of `self.align`; therefore, the call to // `Layout::from_size_align` below should never panic. - Some((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) + Ok((Layout::from_size_align(alloc_size, self.align).unwrap(), padded_size)) } /// Creates a layout describing the record for `self` followed by @@ -251,17 +253,19 @@ impl Layout { /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `None`. - pub fn extend(&self, next: Self) -> Option<(Self, usize)> { + pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { let new_align = cmp::max(self.align, next.align); let realigned = Layout::from_size_align(self.size, new_align)?; let pad = realigned.padding_needed_for(next.align); - let offset = self.size.checked_add(pad)?; - let new_size = offset.checked_add(next.size)?; + let offset = self.size.checked_add(pad) + .ok_or(LayoutErr { private: () })?; + let new_size = offset.checked_add(next.size) + .ok_or(LayoutErr { private: () })?; let layout = Layout::from_size_align(new_size, new_align)?; - Some((layout, offset)) + Ok((layout, offset)) } /// Creates a layout describing the record for `n` instances of @@ -276,8 +280,8 @@ impl Layout { /// aligned. /// /// On arithmetic overflow, returns `None`. - pub fn repeat_packed(&self, n: usize) -> Option { - let size = self.size().checked_mul(n)?; + pub fn repeat_packed(&self, n: usize) -> Result { + let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; Layout::from_size_align(size, self.align) } @@ -296,16 +300,17 @@ impl Layout { /// `extend`.) /// /// On arithmetic overflow, returns `None`. - pub fn extend_packed(&self, next: Self) -> Option<(Self, usize)> { - let new_size = self.size().checked_add(next.size())?; + pub fn extend_packed(&self, next: Self) -> Result<(Self, usize), LayoutErr> { + let new_size = self.size().checked_add(next.size()) + .ok_or(LayoutErr { private: () })?; let layout = Layout::from_size_align(new_size, self.align)?; - Some((layout, self.size())) + Ok((layout, self.size())) } /// Creates a layout describing the record for a `[T; n]`. /// /// On arithmetic overflow, returns `None`. - pub fn array(n: usize) -> Option { + pub fn array(n: usize) -> Result { Layout::new::() .repeat(n) .map(|(k, offs)| { @@ -315,6 +320,20 @@ impl Layout { } } +/// The parameters given to `Layout::from_size_align` do not satisfy +/// its documented constraints. +#[derive(Clone, PartialEq, Eq, Debug)] +pub struct LayoutErr { + private: () +} + +// (we need this for downstream impl of trait Error) +impl fmt::Display for LayoutErr { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str("invalid parameters to Layout::from_size_align") + } +} + /// The `AllocErr` error specifies whether an allocation failure is /// specifically due to resource exhaustion or if it is due to /// something wrong when combining the given input arguments with this @@ -990,7 +1009,7 @@ pub unsafe trait Alloc { where Self: Sized { match Layout::array::(n) { - Some(ref layout) if layout.size() > 0 => { + Ok(ref layout) if layout.size() > 0 => { unsafe { self.alloc(layout.clone()) .map(|p| { @@ -1041,7 +1060,7 @@ pub unsafe trait Alloc { where Self: Sized { match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { - (Some(ref k_old), Some(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { + (Ok(ref k_old), Ok(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) .map(|p| NonNull::new_unchecked(p as *mut T)) } @@ -1076,7 +1095,7 @@ pub unsafe trait Alloc { { let raw_ptr = ptr.as_ptr() as *mut u8; match Layout::array::(n) { - Some(ref k) if k.size() > 0 => { + Ok(ref k) if k.size() > 0 => { Ok(self.dealloc(raw_ptr, k.clone())) } _ => { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index e9bdd4e7d07..50263705143 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -755,7 +755,7 @@ impl RawTable { } let buffer = Global.alloc(Layout::from_size_align(size, alignment) - .ok_or(CollectionAllocErr::CapacityOverflow)?)?; + .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?; let hashes = buffer as *mut HashUint; diff --git a/src/libstd/error.rs b/src/libstd/error.rs index ec55a3c021a..3c209928d43 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -57,7 +57,7 @@ use cell; use char; use core::array; use fmt::{self, Debug, Display}; -use heap::{AllocErr, CannotReallocInPlace}; +use heap::{AllocErr, LayoutErr, CannotReallocInPlace}; use mem::transmute; use num; use str; @@ -247,6 +247,15 @@ impl Error for AllocErr { } } +#[unstable(feature = "allocator_api", + reason = "the precise API and guarantees it provides may be tweaked.", + issue = "32838")] +impl Error for LayoutErr { + fn description(&self) -> &str { + "invalid parameters to Layout::from_size_align" + } +} + #[unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked.", issue = "32838")] -- cgit 1.4.1-3-g733a5 From c957e99b305ecee113442a7ce0edd6b565200ca9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 17:19:16 +0200 Subject: realloc with a new size only, not a full new layout. Changing the alignment with realloc is not supported. --- src/liballoc/alloc.rs | 22 +++++------- src/liballoc/heap.rs | 8 ++--- src/liballoc/raw_vec.rs | 17 +++++---- src/liballoc_system/lib.rs | 42 +++++++++------------- src/libcore/alloc.rs | 87 +++++++++++++++++++--------------------------- 5 files changed, 74 insertions(+), 102 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a7b5864016c..a6fc8d5004c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -91,21 +91,17 @@ unsafe impl Alloc for Global { unsafe fn realloc(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) + new_size: usize) -> Result<*mut u8, AllocErr> { - if layout.align() == new_layout.align() { - #[cfg(not(stage0))] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_layout.size()); - #[cfg(stage0)] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), - new_layout.size(), new_layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + #[cfg(not(stage0))] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_size); + #[cfg(stage0)] + let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + new_size, layout.align(), &mut 0); + + if !ptr.is_null() { + Ok(ptr) } else { Err(AllocErr) } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 765fb8458d1..e79383331e1 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -64,7 +64,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::realloc(self, ptr, layout, new_layout) + CoreAlloc::realloc(self, ptr, layout, new_layout.size()) } unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { @@ -79,20 +79,20 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - CoreAlloc::realloc_excess(self, ptr, layout, new_layout) + CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) } unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - CoreAlloc::grow_in_place(self, ptr, layout, new_layout) + CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - CoreAlloc::shrink_in_place(self, ptr, layout, new_layout) + CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 18aaf1de08e..80b816878fb 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -309,11 +309,10 @@ impl RawVec { // `from_size_align_unchecked`. let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; - let new_layout = Layout::from_size_align_unchecked(new_size, cur.align()); alloc_guard(new_size).expect("capacity overflow"); let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, cur, - new_layout); + new_size); match ptr_res { Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), Err(_) => self.a.oom(), @@ -371,8 +370,7 @@ impl RawVec { let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); let ptr = self.ptr() as *mut _; - let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); - match self.a.grow_in_place(ptr, old_layout, new_layout) { + match self.a.grow_in_place(ptr, old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -428,8 +426,9 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout) + self.a.realloc(old_ptr, layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -537,8 +536,9 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout) + self.a.realloc(old_ptr, layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -604,7 +604,7 @@ impl RawVec { let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); - match self.a.grow_in_place(ptr, old_layout, new_layout) { + match self.a.grow_in_place(ptr, old_layout, new_layout.size()) { Ok(_) => { self.cap = new_cap; true @@ -664,10 +664,9 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - let new_layout = Layout::from_size_align_unchecked(new_size, align); match self.a.realloc(self.ptr.as_ptr() as *mut u8, old_layout, - new_layout) { + new_size) { Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), Err(_) => self.a.oom(), } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 48a6c1e150a..7b788a5f989 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -69,8 +69,8 @@ unsafe impl Alloc for System { unsafe fn realloc(&mut self, ptr: *mut u8, old_layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - Alloc::realloc(&mut &*self, ptr, old_layout, new_layout) + new_size: usize) -> Result<*mut u8, AllocErr> { + Alloc::realloc(&mut &*self, ptr, old_layout, new_size) } fn oom(&mut self) -> ! { @@ -91,24 +91,24 @@ unsafe impl Alloc for System { unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result { - Alloc::realloc_excess(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result { + Alloc::realloc_excess(&mut &*self, ptr, layout, new_size) } #[inline] unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - Alloc::grow_in_place(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + Alloc::grow_in_place(&mut &*self, ptr, layout, new_size) } #[inline] unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - Alloc::shrink_in_place(&mut &*self, ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + Alloc::shrink_in_place(&mut &*self, ptr, layout, new_size) } } @@ -166,12 +166,8 @@ macro_rules! alloc_methods_based_on_global_alloc { unsafe fn realloc(&mut self, ptr: *mut u8, old_layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - if old_layout.align() != new_layout.align() { - return Err(AllocErr) - } - - let ptr = GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_layout.size()); + new_size: usize) -> Result<*mut u8, AllocErr> { + let ptr = GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_size); if !ptr.is_null() { Ok(ptr as *mut u8) } else { @@ -428,30 +424,26 @@ mod platform { unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - self.shrink_in_place(ptr, layout, new_layout) + new_size: usize) -> Result<(), CannotReallocInPlace> { + self.shrink_in_place(ptr, layout, new_size) } #[inline] unsafe fn shrink_in_place(&mut self, ptr: *mut u8, - old_layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - if old_layout.align() != new_layout.align() { - return Err(CannotReallocInPlace) - } - - let new = if new_layout.align() <= MIN_ALIGN { + layout: Layout, + new_size: usize) -> Result<(), CannotReallocInPlace> { + let new = if layout.align() <= MIN_ALIGN { HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, ptr as LPVOID, - new_layout.size()) + new_size) } else { let header = get_header(ptr); HeapReAlloc(GetProcessHeap(), HEAP_REALLOC_IN_PLACE_ONLY, header.0 as LPVOID, - new_layout.size() + new_layout.align()) + new_size + layout.align()) }; if new.is_null() { Err(CannotReallocInPlace) diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 0acaf54e0d9..757f06e731f 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -633,9 +633,10 @@ pub unsafe trait Alloc { // realloc. alloc_excess, realloc_excess /// Returns a pointer suitable for holding data described by - /// `new_layout`, meeting its size and alignment guarantees. To + /// a new layout with `layout`’s alginment and a size given + /// by `new_size`. To /// accomplish this, this may extend or shrink the allocation - /// referenced by `ptr` to fit `new_layout`. + /// referenced by `ptr` to fit the new layout. /// /// If this returns `Ok`, then ownership of the memory block /// referenced by `ptr` has been transferred to this @@ -648,12 +649,6 @@ pub unsafe trait Alloc { /// block has not been transferred to this allocator, and the /// contents of the memory block are unaltered. /// - /// For best results, `new_layout` should not impose a different - /// alignment constraint than `layout`. (In other words, - /// `new_layout.align()` should equal `layout.align()`.) However, - /// behavior is well-defined (though underspecified) when this - /// constraint is violated; further discussion below. - /// /// # Safety /// /// This function is unsafe because undefined behavior can result @@ -661,12 +656,13 @@ pub unsafe trait Alloc { /// /// * `ptr` must be currently allocated via this allocator, /// - /// * `layout` must *fit* the `ptr` (see above). (The `new_layout` + /// * `layout` must *fit* the `ptr` (see above). (The `new_size` /// argument need not fit it.) /// - /// * `new_layout` must have size greater than zero. + /// * `new_size` must be greater than zero. /// - /// * the alignment of `new_layout` is non-zero. + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, + /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). /// /// (Extension subtraits might provide more specific bounds on /// behavior, e.g. guarantee a sentinel address or a null pointer @@ -674,18 +670,11 @@ pub unsafe trait Alloc { /// /// # Errors /// - /// Returns `Err` only if `new_layout` does not match the - /// alignment of `layout`, or does not meet the allocator's size + /// Returns `Err` only if the new layout + /// does not meet the allocator's size /// and alignment constraints of the allocator, or if reallocation /// otherwise fails. /// - /// (Note the previous sentence did not say "if and only if" -- in - /// particular, an implementation of this method *can* return `Ok` - /// if `new_layout.align() != old_layout.align()`; or it can - /// return `Err` in that scenario, depending on whether this - /// allocator can dynamically adjust the alignment constraint for - /// the block.) - /// /// Implementations are encouraged to return `Err` on memory /// exhaustion rather than panicking or aborting, but this is not /// a strict requirement. (Specifically: it is *legal* to @@ -698,22 +687,21 @@ pub unsafe trait Alloc { unsafe fn realloc(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - let new_size = new_layout.size(); + new_size: usize) -> Result<*mut u8, AllocErr> { let old_size = layout.size(); - let aligns_match = layout.align == new_layout.align; - if new_size >= old_size && aligns_match { - if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_layout.clone()) { + if new_size >= old_size { + if let Ok(()) = self.grow_in_place(ptr, layout.clone(), new_size) { return Ok(ptr); } - } else if new_size < old_size && aligns_match { - if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_layout.clone()) { + } else if new_size < old_size { + if let Ok(()) = self.shrink_in_place(ptr, layout.clone(), new_size) { return Ok(ptr); } } // otherwise, fall back on alloc + copy + dealloc. + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); @@ -789,17 +777,19 @@ pub unsafe trait Alloc { unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result { + new_size: usize) -> Result { + let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let usable_size = self.usable_size(&new_layout); - self.realloc(ptr, layout, new_layout) + self.realloc(ptr, layout, new_size) .map(|p| Excess(p, usable_size.1)) } - /// Attempts to extend the allocation referenced by `ptr` to fit `new_layout`. + /// Attempts to extend the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and thus can - /// be used to carry data of that layout. (The allocator is allowed to + /// memory block referenced by `ptr` now fits `new_size`, and thus can + /// be used to carry data of a layout of that size and same alignment as + /// `layout`. (The allocator is allowed to /// expend effort to accomplish this, such as extending the memory block to /// include successor blocks, or virtual memory tricks.) /// @@ -815,11 +805,9 @@ pub unsafe trait Alloc { /// * `ptr` must be currently allocated via this allocator, /// /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, + /// `new_size` argument need not fit it, /// - /// * `new_layout.size()` must not be less than `layout.size()`, - /// - /// * `new_layout.align()` must equal `layout.align()`. + /// * `new_size` must not be less than `layout.size()`, /// /// # Errors /// @@ -834,24 +822,23 @@ pub unsafe trait Alloc { unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { + new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size >= layout.size); - debug_assert!(new_layout.align == layout.align); + debug_assert!(new_size >= layout.size); let (_l, u) = self.usable_size(&layout); // _l <= layout.size() [guaranteed by usable_size()] // layout.size() <= new_layout.size() [required by this method] - if new_layout.size <= u { + if new_size <= u { return Ok(()); } else { return Err(CannotReallocInPlace); } } - /// Attempts to shrink the allocation referenced by `ptr` to fit `new_layout`. + /// Attempts to shrink the allocation referenced by `ptr` to fit `new_size`. /// /// If this returns `Ok`, then the allocator has asserted that the - /// memory block referenced by `ptr` now fits `new_layout`, and + /// memory block referenced by `ptr` now fits `new_size`, and /// thus can only be used to carry data of that smaller /// layout. (The allocator is allowed to take advantage of this, /// carving off portions of the block for reuse elsewhere.) The @@ -872,13 +859,11 @@ pub unsafe trait Alloc { /// * `ptr` must be currently allocated via this allocator, /// /// * `layout` must *fit* the `ptr` (see above); note the - /// `new_layout` argument need not fit it, + /// `new_size` argument need not fit it, /// - /// * `new_layout.size()` must not be greater than `layout.size()` + /// * `new_size` must not be greater than `layout.size()` /// (and must be greater than zero), /// - /// * `new_layout.align()` must equal `layout.align()`. - /// /// # Errors /// /// Returns `Err(CannotReallocInPlace)` when the allocator is @@ -892,14 +877,13 @@ pub unsafe trait Alloc { unsafe fn shrink_in_place(&mut self, ptr: *mut u8, layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { + new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. - debug_assert!(new_layout.size <= layout.size); - debug_assert!(new_layout.align == layout.align); + debug_assert!(new_size <= layout.size); let (l, _u) = self.usable_size(&layout); // layout.size() <= _u [guaranteed by usable_size()] // new_layout.size() <= layout.size() [required by this method] - if l <= new_layout.size { + if l <= new_size { return Ok(()); } else { return Err(CannotReallocInPlace); @@ -1061,7 +1045,8 @@ pub unsafe trait Alloc { { match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { (Ok(ref k_old), Ok(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { - self.realloc(ptr as *mut u8, k_old.clone(), k_new.clone()) + debug_assert!(k_old.align() == k_new.align()); + self.realloc(ptr as *mut u8, k_old.clone(), k_new.size()) .map(|p| NonNull::new_unchecked(p as *mut T)) } _ => { -- cgit 1.4.1-3-g733a5 From eae0d468932660ca383e35bb9d8b0cb4943a82ae Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 18:57:48 +0200 Subject: Restore Global.oom() functionality MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … now that #[global_allocator] does not define a symbol for it --- src/Cargo.lock | 1 + src/liballoc/alloc.rs | 16 ++++++++++++++++ src/liballoc/lib.rs | 1 + src/liballoc_jemalloc/Cargo.toml | 1 + src/liballoc_jemalloc/lib.rs | 10 ++++++++++ src/liballoc_system/lib.rs | 4 ++++ src/libcore/alloc.rs | 4 ++++ src/librustc_allocator/expand.rs | 5 +++++ src/librustc_allocator/lib.rs | 6 ++++++ src/librustc_trans/allocator.rs | 2 ++ src/libstd/alloc.rs | 6 ++++++ src/test/compile-fail/allocator/not-an-allocator.rs | 1 + 12 files changed, 57 insertions(+) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index 2e969f4ec2b..e5297d1482e 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,6 +19,7 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ + "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.9 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a6fc8d5004c..beae52726a6 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -26,6 +26,9 @@ 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] @@ -44,6 +47,9 @@ extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize) -> *mut u8; + #[cold] + #[rustc_allocator_nounwind] + fn __rust_oom() -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] @@ -120,6 +126,16 @@ unsafe impl Alloc for Global { Err(AllocErr) } } + + #[inline] + fn oom(&mut self) -> ! { + unsafe { + #[cfg(not(stage0))] + __rust_oom(); + #[cfg(stage0)] + __rust_oom(&mut 0); + } + } } /// The allocator for unique pointers. diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f6598fe5e89..a10820ebefd 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -97,6 +97,7 @@ #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] +#![feature(libc)] #![feature(needs_allocator)] #![feature(nonzero)] #![feature(optin_builtin_traits)] diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 7986d5dd2eb..02435170374 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,6 +12,7 @@ test = false doc = false [dependencies] +alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 661d7ab78da..2b66c293f21 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,6 +14,7 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] +#![feature(alloc_system)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -22,12 +23,15 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] +extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { + use core::alloc::GlobalAlloc; + use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -96,6 +100,12 @@ mod contents { ptr } + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rde_oom() -> ! { + System.oom() + } + #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 4516664e97c..c6507282b24 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -367,6 +367,7 @@ mod platform { } } +#[inline] fn oom() -> ! { write_to_stderr("fatal runtime error: memory allocation failed"); unsafe { @@ -375,6 +376,7 @@ fn oom() -> ! { } #[cfg(any(unix, target_os = "redox"))] +#[inline] fn write_to_stderr(s: &str) { extern crate libc; @@ -386,6 +388,7 @@ fn write_to_stderr(s: &str) { } #[cfg(windows)] +#[inline] fn write_to_stderr(s: &str) { use core::ptr; @@ -421,4 +424,5 @@ fn write_to_stderr(s: &str) { } #[cfg(not(any(windows, unix, target_os = "redox")))] +#[inline] fn write_to_stderr(_: &str) {} diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index cfa7df06a40..7334f986f2b 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -438,6 +438,10 @@ pub unsafe trait GlobalAlloc { } new_ptr } + + fn oom(&self) -> ! { + unsafe { ::intrinsics::abort() } + } } /// An implementation of `Alloc` can allocate, reallocate, and diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index ce41fe1f3bc..58d4c7f289c 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -231,6 +231,7 @@ impl<'a> AllocFnFactory<'a> { } AllocatorTy::ResultPtr | + AllocatorTy::Bang | AllocatorTy::Unit => { panic!("can't convert AllocatorTy to an argument") } @@ -248,6 +249,10 @@ impl<'a> AllocFnFactory<'a> { (self.ptr_u8(), expr) } + AllocatorTy::Bang => { + (self.cx.ty(self.span, TyKind::Never), expr) + } + AllocatorTy::Unit => { (self.cx.ty(self.span, TyKind::Tup(Vec::new())), expr) } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 969086815de..706eab72d44 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,6 +23,11 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, + AllocatorMethod { + name: "oom", + inputs: &[], + output: AllocatorTy::Bang, + }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], @@ -47,6 +52,7 @@ pub struct AllocatorMethod { } pub enum AllocatorTy { + Bang, Layout, Ptr, ResultPtr, diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs index ffebb959ebf..f2dd2ed8460 100644 --- a/src/librustc_trans/allocator.rs +++ b/src/librustc_trans/allocator.rs @@ -43,11 +43,13 @@ pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) AllocatorTy::Ptr => args.push(i8p), AllocatorTy::Usize => args.push(usize), + AllocatorTy::Bang | AllocatorTy::ResultPtr | AllocatorTy::Unit => panic!("invalid allocator arg"), } } let output = match method.output { + AllocatorTy::Bang => None, AllocatorTy::ResultPtr => Some(i8p), AllocatorTy::Unit => None, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 335dc7e0412..4e728df010a 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -35,6 +35,12 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } + #[no_mangle] + #[rustc_std_internal_symbol] + pub unsafe extern fn __rdl_oom() -> ! { + System.oom() + } + #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index 140cad22f34..1479d0b6264 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -16,5 +16,6 @@ static A: usize = 0; //~| the trait bound `usize: //~| the trait bound `usize: //~| the trait bound `usize: +//~| the trait bound `usize: fn main() {} -- cgit 1.4.1-3-g733a5 From fd242ee64c5488e64e2bb677d90f2460e017b7cb Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 4 Apr 2018 19:15:22 +0200 Subject: impl GlobalAlloc for Global --- src/liballoc/alloc.rs | 85 ++++++++++++++++++++++++++++++--------------------- 1 file changed, 50 insertions(+), 35 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index beae52726a6..063f0543ec4 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -73,62 +73,42 @@ pub type Heap = Global; #[allow(non_upper_case_globals)] pub const Heap: Global = Global; -unsafe impl Alloc for Global { +unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&self, layout: Layout) -> *mut Void { #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_size: usize) - -> Result<*mut u8, AllocErr> - { + unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { #[cfg(not(stage0))] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), new_size); + let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); #[cfg(stage0)] - let ptr = __rust_realloc(ptr, layout.size(), layout.align(), + let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size, layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - - if !ptr.is_null() { - Ok(ptr) - } else { - Err(AllocErr) - } + ptr as *mut Void } #[inline] - fn oom(&mut self) -> ! { + fn oom(&self) -> ! { unsafe { #[cfg(not(stage0))] __rust_oom(); @@ -138,6 +118,38 @@ unsafe impl Alloc for Global { } } +unsafe impl Alloc for Global { + #[inline] + unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalAlloc::alloc(self, layout).into() + } + + #[inline] + unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + } + + #[inline] + unsafe fn realloc(&mut self, + ptr: *mut u8, + layout: Layout, + new_size: usize) + -> Result<*mut u8, AllocErr> + { + GlobalAlloc::realloc(self, ptr as *mut Void, layout, new_size).into() + } + + #[inline] + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + GlobalAlloc::alloc_zeroed(self, layout).into() + } + + #[inline] + fn oom(&mut self) -> ! { + GlobalAlloc::oom(self) + } +} + /// The allocator for unique pointers. // This function must not unwind. If it does, MIR trans will fail. #[cfg(not(test))] @@ -148,9 +160,12 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - Global.alloc(layout).unwrap_or_else(|_| { + let ptr = Global.alloc(layout); + if !ptr.is_null() { + ptr as *mut u8 + } else { Global.oom() - }) + } } } @@ -162,7 +177,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut u8, layout); + Global.dealloc(ptr as *mut Void, layout); } } -- cgit 1.4.1-3-g733a5 From fddf51ee0b9765484fc316dbf3d4feb8ceea715d Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 3 Apr 2018 08:51:02 +0900 Subject: Use NonNull instead of *mut u8 in the Alloc trait Fixes #49608 --- src/doc/nomicon | 2 +- .../src/language-features/global-allocator.md | 1 + src/liballoc/alloc.rs | 19 +++---- src/liballoc/arc.rs | 16 +++--- src/liballoc/btree/node.rs | 16 +++--- src/liballoc/heap.rs | 22 ++++++-- src/liballoc/lib.rs | 1 + src/liballoc/raw_vec.rs | 40 +++++++-------- src/liballoc/rc.rs | 18 +++---- src/liballoc/tests/heap.rs | 3 +- src/liballoc_system/lib.rs | 29 +++++------ src/libcore/alloc.rs | 58 ++++++++++------------ src/libcore/ptr.rs | 8 +++ src/libstd/collections/hash/table.rs | 6 +-- src/libstd/lib.rs | 1 + src/test/run-pass/allocator/xcrate-use2.rs | 2 +- src/test/run-pass/realloc-16687.rs | 18 +++---- src/test/run-pass/regions-mock-trans.rs | 5 +- 18 files changed, 136 insertions(+), 129 deletions(-) (limited to 'src/liballoc') diff --git a/src/doc/nomicon b/src/doc/nomicon index 6a8f0a27e9a..498ac299742 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 6a8f0a27e9a58c55c89d07bc43a176fdae5e051c +Subproject commit 498ac2997420f7b25f7cd0a3f8202950d8ad93ec diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index 6ce12ba684d..a3f3ee65bf0 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -30,6 +30,7 @@ looks like: #![feature(global_allocator, allocator_api, heap_api)] use std::alloc::{GlobalAlloc, System, Layout, Void}; +use std::ptr::NonNull; struct MyAllocator; diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 063f0543ec4..af48aa7961e 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,6 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; +use core::ptr::NonNull; use core::usize; #[doc(inline)] @@ -120,27 +121,27 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result<*mut u8, AllocErr> + -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr as *mut Void, layout, new_size).into() + GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(self, layout).into() } @@ -195,8 +196,8 @@ mod tests { let ptr = Global.alloc_zeroed(layout.clone()) .unwrap_or_else(|_| Global.oom()); - let end = ptr.offset(layout.size() as isize); - let mut i = ptr; + let mut i = ptr.cast::().as_ptr(); + let end = i.offset(layout.size() as isize); while i < end { assert_eq!(*i, 0); i = i.offset(1); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index f0a325530ba..88754ace3ce 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -512,15 +512,13 @@ impl Arc { // Non-inlined part of `drop`. #[inline(never)] unsafe fn drop_slow(&mut self) { - let ptr = self.ptr.as_ptr(); - // Destroy the data at this time, even though we may not free the box // allocation itself (there may still be weak pointers lying around). ptr::drop_in_place(&mut self.ptr.as_mut().data); if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) } } @@ -558,7 +556,7 @@ impl Arc { .unwrap_or_else(|_| Global.oom()); // Initialize the real ArcInner - let inner = set_data_ptr(ptr as *mut T, mem) as *mut ArcInner; + let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); @@ -625,7 +623,7 @@ impl ArcFromSlice for Arc<[T]> { // In the event of a panic, elements that have been written // into the new ArcInner will be dropped, then the memory freed. struct Guard { - mem: *mut u8, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -639,7 +637,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem, self.layout.clone()); + Global.dealloc(self.mem.as_void(), self.layout.clone()); } } } @@ -655,7 +653,7 @@ impl ArcFromSlice for Arc<[T]> { let elems = &mut (*ptr).data as *mut [T] as *mut T; let mut guard = Guard{ - mem: mem, + mem: NonNull::new_unchecked(mem), elems: elems, layout: layout, n_elems: 0, @@ -1147,8 +1145,6 @@ impl Drop for Weak { /// assert!(other_weak_foo.upgrade().is_none()); /// ``` fn drop(&mut self) { - let ptr = self.ptr.as_ptr(); - // If we find out that we were the last weak pointer, then its time to // deallocate the data entirely. See the discussion in Arc::drop() about // the memory orderings @@ -1160,7 +1156,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)) + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 8e23228bd28..64aa40ac166 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -236,7 +236,7 @@ impl Root { pub fn pop_level(&mut self) { debug_assert!(self.height > 0); - let top = self.node.ptr.as_ptr() as *mut u8; + let top = self.node.ptr; self.node = unsafe { BoxedNode::from_ptr(self.as_mut() @@ -249,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(top, Layout::new::>()); + Global.dealloc(NonNull::from(top).as_void(), Layout::new::>()); } } } @@ -433,9 +433,9 @@ impl NodeRef { marker::Edge > > { - let ptr = self.as_leaf() as *const LeafNode as *const u8 as *mut u8; + let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(ptr, Layout::new::>()); + Global.dealloc(node.as_void(), Layout::new::>()); ret } } @@ -454,9 +454,9 @@ impl NodeRef { marker::Edge > > { - let ptr = self.as_internal() as *const InternalNode as *const u8 as *mut u8; + let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(ptr, Layout::new::>()); + Global.dealloc(node.as_void(), Layout::new::>()); ret } } @@ -1239,12 +1239,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_ptr() as *mut u8, + right_node.node.as_void(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_ptr() as *mut u8, + right_node.node.as_void(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index e79383331e1..cfb6504e743 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -8,14 +8,20 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -pub use alloc::{Excess, Layout, AllocErr, CannotReallocInPlace}; +#![allow(deprecated)] + +pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Void}; use core::alloc::Alloc as CoreAlloc; +use core::ptr::NonNull; #[doc(hidden)] pub mod __core { pub use core::*; } +#[derive(Debug)] +pub struct Excess(pub *mut u8, pub usize); + /// Compatibility with older versions of #[global_allocator] during bootstrap pub unsafe trait Alloc { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; @@ -42,13 +48,13 @@ pub unsafe trait Alloc { new_layout: Layout) -> Result<(), CannotReallocInPlace>; } -#[allow(deprecated)] unsafe impl Alloc for T where T: CoreAlloc { unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc(self, layout) + CoreAlloc::alloc(self, layout).map(|ptr| ptr.cast().as_ptr()) } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::dealloc(self, ptr, layout) } @@ -64,28 +70,33 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::realloc(self, ptr, layout, new_layout.size()) + let ptr = NonNull::new_unchecked(ptr as *mut Void); + CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc_zeroed(self, layout) + CoreAlloc::alloc_zeroed(self, layout).map(|ptr| ptr.cast().as_ptr()) } unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { CoreAlloc::alloc_excess(self, layout) + .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } unsafe fn realloc_excess(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) + .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } unsafe fn grow_in_place(&mut self, ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -93,6 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { + let ptr = NonNull::new_unchecked(ptr as *mut Void); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index a10820ebefd..3a106a2ff5c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -99,6 +99,7 @@ #![feature(lang_items)] #![feature(libc)] #![feature(needs_allocator)] +#![feature(nonnull_cast)] #![feature(nonzero)] #![feature(optin_builtin_traits)] #![feature(pattern)] diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 80b816878fb..d72301f5ad6 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -12,7 +12,7 @@ use alloc::{Alloc, Layout, Global}; use core::cmp; use core::mem; use core::ops::Drop; -use core::ptr::{self, Unique}; +use core::ptr::{self, NonNull, Unique}; use core::slice; use super::boxed::Box; use super::allocator::CollectionAllocErr; @@ -90,7 +90,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - mem::align_of::() as *mut u8 + NonNull::::dangling().as_void() } else { let align = mem::align_of::(); let result = if zeroed { @@ -105,7 +105,7 @@ impl RawVec { }; RawVec { - ptr: Unique::new_unchecked(ptr as *mut _), + ptr: ptr.cast().into(), cap, a, } @@ -310,11 +310,11 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr_res = self.a.realloc(self.ptr.as_ptr() as *mut u8, + let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_void(), cur, new_size); match ptr_res { - Ok(ptr) => (new_cap, Unique::new_unchecked(ptr as *mut T)), + Ok(ptr) => (new_cap, ptr.cast().into()), Err(_) => self.a.oom(), } } @@ -369,8 +369,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr = self.ptr() as *mut _; - match self.a.grow_in_place(ptr, old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).as_void(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -427,13 +426,12 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; - self.ptr = Unique::new_unchecked(res? as *mut T); + self.ptr = res?.cast().into(); self.cap = new_cap; Ok(()) @@ -537,13 +535,12 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - let old_ptr = self.ptr.as_ptr() as *mut u8; - self.a.realloc(old_ptr, layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; - self.ptr = Unique::new_unchecked(res? as *mut T); + self.ptr = res?.cast().into(); self.cap = new_cap; Ok(()) @@ -600,11 +597,12 @@ impl RawVec { // (regardless of whether `self.cap - used_cap` wrapped). // Therefore we can safely call grow_in_place. - let ptr = self.ptr() as *mut _; let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); - match self.a.grow_in_place(ptr, old_layout, new_layout.size()) { + match self.a.grow_in_place( + NonNull::from(self.ptr).as_void(), old_layout, new_layout.size(), + ) { Ok(_) => { self.cap = new_cap; true @@ -664,10 +662,10 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(self.ptr.as_ptr() as *mut u8, + match self.a.realloc(NonNull::from(self.ptr).as_void(), old_layout, new_size) { - Ok(p) => self.ptr = Unique::new_unchecked(p as *mut T), + Ok(p) => self.ptr = p.cast().into(), Err(_) => self.a.oom(), } } @@ -700,8 +698,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - let ptr = self.ptr() as *mut u8; - self.a.dealloc(ptr, layout); + self.a.dealloc(NonNull::from(self.ptr).as_void(), layout); } } } @@ -737,6 +734,7 @@ fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { #[cfg(test)] mod tests { use super::*; + use alloc::Void; #[test] fn allocator_param() { @@ -756,7 +754,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -766,7 +764,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 3c0b11bfe74..1c835fe50de 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free}; +use alloc::{Global, Alloc, Layout, Void, box_free}; use string::String; use vec::Vec; @@ -671,7 +671,7 @@ impl Rc { .unwrap_or_else(|_| Global.oom()); // Initialize the real RcBox - let inner = set_data_ptr(ptr as *mut T, mem) as *mut RcBox; + let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; ptr::write(&mut (*inner).strong, Cell::new(1)); ptr::write(&mut (*inner).weak, Cell::new(1)); @@ -737,7 +737,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: *mut u8, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -760,14 +760,14 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut u8; + let mem = ptr as *mut _ as *mut Void; let layout = Layout::for_value(&*ptr); // Pointer to first element let elems = &mut (*ptr).value as *mut [T] as *mut T; let mut guard = Guard{ - mem: mem, + mem: NonNull::new_unchecked(mem), elems: elems, layout: layout, n_elems: 0, @@ -834,8 +834,6 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { /// ``` fn drop(&mut self) { unsafe { - let ptr = self.ptr.as_ptr(); - self.dec_strong(); if self.strong() == 0 { // destroy the contained object @@ -846,7 +844,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1266,13 +1264,11 @@ impl Drop for Weak { /// ``` fn drop(&mut self) { unsafe { - let ptr = self.ptr.as_ptr(); - self.dec_weak(); // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(ptr as *mut u8, Layout::for_value(&*ptr)); + Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc/tests/heap.rs b/src/liballoc/tests/heap.rs index 328131e2fef..6fa88ce969a 100644 --- a/src/liballoc/tests/heap.rs +++ b/src/liballoc/tests/heap.rs @@ -34,7 +34,8 @@ fn check_overalign_requests(mut allocator: T) { allocator.alloc(Layout::from_size_align(size, align).unwrap()).unwrap() }).collect(); for &ptr in &pointers { - assert_eq!((ptr as usize) % align, 0, "Got a pointer less aligned than requested") + assert_eq!((ptr.as_ptr() as usize) % align, 0, + "Got a pointer less aligned than requested") } // Clean up diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index c6507282b24..bf27e972177 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -42,6 +42,7 @@ const MIN_ALIGN: usize = 8; const MIN_ALIGN: usize = 16; use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Void}; +use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; @@ -49,26 +50,26 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(self, layout).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, old_layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { - GlobalAlloc::realloc(self, ptr as *mut Void, old_layout, new_size).into() + new_size: usize) -> Result, AllocErr> { + GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size).into() } #[inline] @@ -81,26 +82,26 @@ unsafe impl Alloc for System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc(*self, layout).into() } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { GlobalAlloc::alloc_zeroed(*self, layout).into() } #[inline] - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - GlobalAlloc::dealloc(*self, ptr as *mut Void, layout) + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, old_layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { - GlobalAlloc::realloc(*self, ptr as *mut Void, old_layout, new_size).into() + new_size: usize) -> Result, AllocErr> { + GlobalAlloc::realloc(*self, ptr.as_ptr(), old_layout, new_size).into() } #[inline] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 7334f986f2b..632eed96049 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -42,21 +42,17 @@ impl Void { } /// Convert from a return value of GlobalAlloc::alloc to that of Alloc::alloc -impl From<*mut Void> for Result<*mut u8, AllocErr> { +impl From<*mut Void> for Result, AllocErr> { fn from(ptr: *mut Void) -> Self { - if !ptr.is_null() { - Ok(ptr as *mut u8) - } else { - Err(AllocErr) - } + NonNull::new(ptr).ok_or(AllocErr) } } /// Convert from a return value of Alloc::alloc to that of GlobalAlloc::alloc -impl From> for *mut Void { - fn from(result: Result<*mut u8, AllocErr>) -> Self { +impl From, AllocErr>> for *mut Void { + fn from(result: Result, AllocErr>) -> Self { match result { - Ok(ptr) => ptr as *mut Void, + Ok(ptr) => ptr.as_ptr(), Err(_) => Void::null_mut(), } } @@ -65,7 +61,7 @@ impl From> for *mut Void { /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub *mut u8, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -575,7 +571,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -592,7 +588,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); /// Allocator-specific method for signaling an out-of-memory /// condition. @@ -710,9 +706,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result<*mut u8, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -729,7 +725,9 @@ pub unsafe trait Alloc { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr as *const u8, new_ptr, cmp::min(old_size, new_size)); + ptr::copy_nonoverlapping(ptr.as_ptr() as *const u8, + new_ptr.as_ptr() as *mut u8, + cmp::min(old_size, new_size)); self.dealloc(ptr, layout); } result @@ -751,11 +749,11 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { - ptr::write_bytes(p, 0, size); + ptr::write_bytes(p.as_ptr() as *mut u8, 0, size); } p } @@ -800,7 +798,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -845,7 +843,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -900,7 +898,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -951,7 +949,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - unsafe { self.alloc(k).map(|p| NonNull::new_unchecked(p as *mut T)) } + unsafe { self.alloc(k).map(|p| p.cast()) } } else { Err(AllocErr) } @@ -977,10 +975,9 @@ pub unsafe trait Alloc { unsafe fn dealloc_one(&mut self, ptr: NonNull) where Self: Sized { - let raw_ptr = ptr.as_ptr() as *mut u8; let k = Layout::new::(); if k.size() > 0 { - self.dealloc(raw_ptr, k); + self.dealloc(ptr.as_void(), k); } } @@ -1020,10 +1017,7 @@ pub unsafe trait Alloc { match Layout::array::(n) { Ok(ref layout) if layout.size() > 0 => { unsafe { - self.alloc(layout.clone()) - .map(|p| { - NonNull::new_unchecked(p as *mut T) - }) + self.alloc(layout.clone()).map(|p| p.cast()) } } _ => Err(AllocErr), @@ -1068,11 +1062,10 @@ pub unsafe trait Alloc { n_new: usize) -> Result, AllocErr> where Self: Sized { - match (Layout::array::(n_old), Layout::array::(n_new), ptr.as_ptr()) { - (Ok(ref k_old), Ok(ref k_new), ptr) if k_old.size() > 0 && k_new.size() > 0 => { + match (Layout::array::(n_old), Layout::array::(n_new)) { + (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr as *mut u8, k_old.clone(), k_new.size()) - .map(|p| NonNull::new_unchecked(p as *mut T)) + self.realloc(ptr.as_void(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1103,10 +1096,9 @@ pub unsafe trait Alloc { unsafe fn dealloc_array(&mut self, ptr: NonNull, n: usize) -> Result<(), AllocErr> where Self: Sized { - let raw_ptr = ptr.as_ptr() as *mut u8; match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(raw_ptr, k.clone())) + Ok(self.dealloc(ptr.as_void(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index c1e150e9fb9..f4e668328ce 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2750,6 +2750,14 @@ impl NonNull { NonNull::new_unchecked(self.as_ptr() as *mut U) } } + + /// Cast to a `Void` pointer + #[unstable(feature = "allocator_api", issue = "32838")] + pub fn as_void(self) -> NonNull<::alloc::Void> { + unsafe { + NonNull::new_unchecked(self.as_ptr() as _) + } + } } #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 50263705143..38c99373788 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -757,12 +757,10 @@ impl RawTable { let buffer = Global.alloc(Layout::from_size_align(size, alignment) .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?; - let hashes = buffer as *mut HashUint; - Ok(RawTable { capacity_mask: capacity.wrapping_sub(1), size: 0, - hashes: TaggedHashUintPtr::new(hashes), + hashes: TaggedHashUintPtr::new(buffer.cast().as_ptr()), marker: marker::PhantomData, }) } @@ -1185,7 +1183,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Global.dealloc(self.hashes.ptr() as *mut u8, + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_void(), Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 25ba75fd35e..a34fcb5a7f9 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -275,6 +275,7 @@ #![feature(macro_reexport)] #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] +#![feature(nonnull_cast)] #![feature(exhaustive_patterns)] #![feature(nonzero)] #![feature(num_bits_bytes)] diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index 52eb963efdb..b8e844522dc 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -30,7 +30,7 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Global.alloc(layout.clone()).unwrap(); + let ptr = Global.alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); Global.dealloc(ptr, layout.clone()); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index a562165d21b..49ab0ee3310 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -13,10 +13,10 @@ // Ideally this would be revised to use no_std, but for now it serves // well enough to reproduce (and illustrate) the bug from #16687. -#![feature(heap_api, allocator_api)] +#![feature(heap_api, allocator_api, nonnull_cast)] -use std::heap::{Heap, Alloc, Layout}; -use std::ptr; +use std::alloc::{Global, Alloc, Layout}; +use std::ptr::{self, NonNull}; fn main() { unsafe { @@ -50,13 +50,13 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Heap.alloc(layout.clone()).unwrap_or_else(|_| Heap.oom()); + let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| Global.oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); } - ret + ret.cast().as_ptr() } unsafe fn deallocate(ptr: *mut u8, layout: Layout) { @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Heap.dealloc(ptr, layout); + Global.dealloc(NonNull::new_unchecked(ptr).as_void(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,14 +72,14 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Heap.realloc(ptr, old.clone(), new.clone()) - .unwrap_or_else(|_| Heap.oom()); + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_void(), old.clone(), new.size()) + .unwrap_or_else(|_| Global.oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", ptr, old, new, ret); } - ret + ret.cast().as_ptr() } fn idx_to_size(i: usize) -> usize { (i+1) * 10 } diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 7d34b8fd00f..3c37243c8b9 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -13,6 +13,7 @@ #![feature(allocator_api)] use std::heap::{Alloc, Heap, Layout}; +use std::ptr::NonNull; struct arena(()); @@ -33,7 +34,7 @@ fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Heap.alloc(Layout::new::()) .unwrap_or_else(|_| Heap.oom()); - &*(ptr as *const _) + &*(ptr.as_ptr() as *const _) } } @@ -45,7 +46,7 @@ fn g(fcx : &Fcx) { let bcx = Bcx { fcx: fcx }; let bcx2 = h(&bcx); unsafe { - Heap.dealloc(bcx2 as *const _ as *mut _, Layout::new::()); + Heap.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); } } -- cgit 1.4.1-3-g733a5 From ed297777599081d11c4a337cf19c9b1a1112136b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 11 Apr 2018 16:28:37 +0200 Subject: Remove conversions for allocated pointers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit One was now unused, and `NonNull::new(…).ok_or(AllocErr)` feels short enough for the few cases that need the other conversion. --- src/liballoc/alloc.rs | 6 +++--- src/liballoc_system/lib.rs | 16 ++++++++-------- src/libcore/alloc.rs | 17 ----------------- 3 files changed, 11 insertions(+), 28 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index af48aa7961e..031babe5f6d 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -122,7 +122,7 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(self, layout).into() + NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] @@ -137,12 +137,12 @@ unsafe impl Alloc for Global { new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index bf27e972177..7fea6061169 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -51,12 +51,12 @@ pub struct System; unsafe impl Alloc for System { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(self, layout).into() + NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] @@ -67,9 +67,9 @@ unsafe impl Alloc for System { #[inline] unsafe fn realloc(&mut self, ptr: NonNull, - old_layout: Layout, + layout: Layout, new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(self, ptr.as_ptr(), old_layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] @@ -83,12 +83,12 @@ unsafe impl Alloc for System { unsafe impl<'a> Alloc for &'a System { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc(*self, layout).into() + NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - GlobalAlloc::alloc_zeroed(*self, layout).into() + NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) } #[inline] @@ -99,9 +99,9 @@ unsafe impl<'a> Alloc for &'a System { #[inline] unsafe fn realloc(&mut self, ptr: NonNull, - old_layout: Layout, + layout: Layout, new_size: usize) -> Result, AllocErr> { - GlobalAlloc::realloc(*self, ptr.as_ptr(), old_layout, new_size).into() + NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 632eed96049..97a49703baf 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -41,23 +41,6 @@ impl Void { } } -/// Convert from a return value of GlobalAlloc::alloc to that of Alloc::alloc -impl From<*mut Void> for Result, AllocErr> { - fn from(ptr: *mut Void) -> Self { - NonNull::new(ptr).ok_or(AllocErr) - } -} - -/// Convert from a return value of Alloc::alloc to that of GlobalAlloc::alloc -impl From, AllocErr>> for *mut Void { - fn from(result: Result, AllocErr>) -> Self { - match result { - Ok(ptr) => ptr.as_ptr(), - Err(_) => Void::null_mut(), - } - } -} - /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -- cgit 1.4.1-3-g733a5 From f607a3872addf380846cae28661a777ec3e3c9a2 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 11 Apr 2018 17:19:48 +0200 Subject: Rename alloc::Void to alloc::Opaque --- src/doc/nomicon | 2 +- .../src/language-features/global-allocator.md | 6 +- src/liballoc/alloc.rs | 26 +++--- src/liballoc/arc.rs | 6 +- src/liballoc/btree/node.rs | 10 +- src/liballoc/heap.rs | 12 +-- src/liballoc/raw_vec.rs | 22 ++--- src/liballoc/rc.rs | 10 +- src/liballoc_system/lib.rs | 103 ++++++++++----------- src/libcore/alloc.rs | 38 ++++---- src/libcore/ptr.rs | 4 +- src/librustc_allocator/expand.rs | 12 +-- src/libstd/alloc.rs | 6 +- src/libstd/collections/hash/table.rs | 2 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 4 +- src/test/run-pass/allocator/auxiliary/custom.rs | 6 +- src/test/run-pass/allocator/custom.rs | 6 +- src/test/run-pass/realloc-16687.rs | 4 +- 18 files changed, 139 insertions(+), 140 deletions(-) (limited to 'src/liballoc') diff --git a/src/doc/nomicon b/src/doc/nomicon index 498ac299742..3c56329d1bd 160000 --- a/src/doc/nomicon +++ b/src/doc/nomicon @@ -1 +1 @@ -Subproject commit 498ac2997420f7b25f7cd0a3f8202950d8ad93ec +Subproject commit 3c56329d1bd9038e5341f1962bcd8d043312a712 diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index a3f3ee65bf0..031b6347445 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,17 +29,17 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::alloc::{GlobalAlloc, System, Layout, Void}; +use std::alloc::{GlobalAlloc, System, Layout, Opaque}; use std::ptr::NonNull; struct MyAllocator; unsafe impl GlobalAlloc for MyAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 031babe5f6d..68a617e0ffe 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -76,36 +76,36 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); #[cfg(stage0)] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size, layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); #[cfg(stage0)] let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); - ptr as *mut Void + ptr as *mut Opaque } #[inline] @@ -121,27 +121,27 @@ unsafe impl GlobalAlloc for Global { unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result, AllocErr> + -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } @@ -178,7 +178,7 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut Void, layout); + Global.dealloc(ptr as *mut Opaque, layout); } } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 88754ace3ce..225b055d8ee 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -518,7 +518,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) } } @@ -637,7 +637,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem.as_void(), self.layout.clone()); + Global.dealloc(self.mem.as_opaque(), self.layout.clone()); } } } @@ -1156,7 +1156,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 64aa40ac166..d6346662314 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -249,7 +249,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(NonNull::from(top).as_void(), Layout::new::>()); + Global.dealloc(NonNull::from(top).as_opaque(), Layout::new::>()); } } } @@ -435,7 +435,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_void(), Layout::new::>()); + Global.dealloc(node.as_opaque(), Layout::new::>()); ret } } @@ -456,7 +456,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_void(), Layout::new::>()); + Global.dealloc(node.as_opaque(), Layout::new::>()); ret } } @@ -1239,12 +1239,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_void(), + right_node.node.as_opaque(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_void(), + right_node.node.as_opaque(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index cfb6504e743..faac38ca7ce 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -10,7 +10,7 @@ #![allow(deprecated)] -pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Void}; +pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Opaque}; use core::alloc::Alloc as CoreAlloc; use core::ptr::NonNull; @@ -54,7 +54,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::dealloc(self, ptr, layout) } @@ -70,7 +70,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } @@ -87,7 +87,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } @@ -96,7 +96,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -104,7 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Void); + let ptr = NonNull::new_unchecked(ptr as *mut Opaque); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d72301f5ad6..214cc7d7d0c 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -90,7 +90,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - NonNull::::dangling().as_void() + NonNull::::dangling().as_opaque() } else { let align = mem::align_of::(); let result = if zeroed { @@ -310,7 +310,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_void(), + let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_opaque(), cur, new_size); match ptr_res { @@ -369,7 +369,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).expect("capacity overflow"); - match self.a.grow_in_place(NonNull::from(self.ptr).as_void(), old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -426,7 +426,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -535,7 +535,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_void(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -601,7 +601,7 @@ impl RawVec { // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).expect("capacity overflow"); match self.a.grow_in_place( - NonNull::from(self.ptr).as_void(), old_layout, new_layout.size(), + NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), ) { Ok(_) => { self.cap = new_cap; @@ -662,7 +662,7 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).as_void(), + match self.a.realloc(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), @@ -698,7 +698,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - self.a.dealloc(NonNull::from(self.ptr).as_void(), layout); + self.a.dealloc(NonNull::from(self.ptr).as_opaque(), layout); } } } @@ -734,7 +734,7 @@ fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { #[cfg(test)] mod tests { use super::*; - use alloc::Void; + use alloc::Opaque; #[test] fn allocator_param() { @@ -754,7 +754,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -764,7 +764,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 1c835fe50de..de0422d82bb 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Void, box_free}; +use alloc::{Global, Alloc, Layout, Opaque, box_free}; use string::String; use vec::Vec; @@ -737,7 +737,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: NonNull, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -760,7 +760,7 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut Void; + let mem = ptr as *mut _ as *mut Opaque; let layout = Layout::for_value(&*ptr); // Pointer to first element @@ -844,7 +844,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1268,7 +1268,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(self.ptr.as_void(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 7fea6061169..fd8109e2a4a 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Void}; +use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Opaque}; use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] @@ -50,25 +50,25 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } @@ -82,25 +82,25 @@ unsafe impl Alloc for System { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl<'a> Alloc for &'a System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } @@ -112,13 +112,13 @@ unsafe impl<'a> Alloc for &'a System { #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { - use core::alloc::{GlobalAlloc, Void, Layout}; + use core::alloc::{GlobalAlloc, Opaque, Layout}; use core::cmp; use core::ptr; impl super::System { - pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Void, old_layout: Layout, - new_size: usize) -> *mut Void { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Opaque, old_layout: Layout, + new_size: usize) -> *mut Opaque { // Docs for GlobalAlloc::realloc require this to be valid: let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); @@ -141,20 +141,21 @@ mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Layout, Void}; + use core::alloc::{GlobalAlloc, Layout, Opaque}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut Void + libc::malloc(layout.size()) as *mut Opaque } else { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - // FIXME: use Void::null_mut https://github.com/rust-lang/rust/issues/49659 - return 0 as *mut Void + // FIXME: use Opaque::null_mut + // https://github.com/rust-lang/rust/issues/49659 + return 0 as *mut Opaque } } aligned_malloc(&layout) @@ -162,9 +163,9 @@ mod platform { } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut Void + libc::calloc(layout.size(), 1) as *mut Opaque } else { let ptr = self.alloc(layout.clone()); if !ptr.is_null() { @@ -175,24 +176,23 @@ mod platform { } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, _layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, _layout: Layout) { libc::free(ptr as *mut libc::c_void) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { - let align = old_layout.align(); - if align <= MIN_ALIGN && align <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + if layout.align() <= MIN_ALIGN && layout.align() <= new_size { + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Opaque } else { - self.realloc_fallback(ptr, old_layout, new_size) + self.realloc_fallback(ptr, layout, new_size) } } } #[cfg(any(target_os = "android", target_os = "redox", target_os = "solaris"))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Void { + unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use // `memalign`, but this unfortunately has the property on some systems @@ -210,19 +210,19 @@ mod platform { // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ // /memory/aligned_memory.cc - libc::memalign(layout.align(), layout.size()) as *mut Void + libc::memalign(layout.align(), layout.size()) as *mut Opaque } #[cfg(not(any(target_os = "android", target_os = "redox", target_os = "solaris")))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Void { + unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { let mut out = ptr::null_mut(); let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); if ret != 0 { - // FIXME: use Void::null_mut https://github.com/rust-lang/rust/issues/49659 - 0 as *mut Void + // FIXME: use Opaque::null_mut https://github.com/rust-lang/rust/issues/49659 + 0 as *mut Opaque } else { - out as *mut Void + out as *mut Opaque } } } @@ -232,7 +232,7 @@ mod platform { mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Void, Layout}; + use core::alloc::{GlobalAlloc, Opaque, Layout}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -264,7 +264,7 @@ mod platform { } #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Void { + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Opaque { let ptr = if layout.align() <= MIN_ALIGN { HeapAlloc(GetProcessHeap(), flags, layout.size()) } else { @@ -276,23 +276,23 @@ mod platform { align_ptr(ptr, layout.align()) } }; - ptr as *mut Void + ptr as *mut Opaque } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { allocate_with_flags(layout, 0) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { allocate_with_flags(layout, HEAP_ZERO_MEMORY) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { if layout.align() <= MIN_ALIGN { let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", @@ -306,12 +306,11 @@ mod platform { } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { - let align = old_layout.align(); - if align <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + if layout.align() <= MIN_ALIGN { + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Opaque } else { - self.realloc_fallback(ptr, old_layout, new_size) + self.realloc_fallback(ptr, layout, new_size) } } } @@ -338,7 +337,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::alloc::{GlobalAlloc, Layout, Void}; + use core::alloc::{GlobalAlloc, Layout, Opaque}; use System; // No need for synchronization here as wasm is currently single-threaded @@ -347,23 +346,23 @@ mod platform { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Void { - DLMALLOC.malloc(layout.size(), layout.align()) as *mut Void + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + DLMALLOC.malloc(layout.size(), layout.align()) as *mut Opaque } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { - DLMALLOC.calloc(layout.size(), layout.align()) as *mut Void + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + DLMALLOC.calloc(layout.size(), layout.align()) as *mut Opaque } #[inline] - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { DLMALLOC.free(ptr as *mut u8, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Void, layout: Layout, new_size: usize) -> *mut Void { - DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Void + unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Opaque } } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 97a49703baf..fdba91bec80 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -24,12 +24,12 @@ use ptr::{self, NonNull}; extern { /// An opaque, unsized type. Used for pointers to allocated memory. /// - /// This type can only be used behind a pointer like `*mut Void` or `ptr::NonNull`. + /// This type can only be used behind a pointer like `*mut Opaque` or `ptr::NonNull`. /// Such pointers are similar to C’s `void*` type. - pub type Void; + pub type Opaque; } -impl Void { +impl Opaque { /// Similar to `std::ptr::null`, which requires `T: Sized`. pub fn null() -> *const Self { 0 as _ @@ -44,7 +44,7 @@ impl Void { /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub NonNull, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -387,11 +387,11 @@ impl From for CollectionAllocErr { // FIXME: docs pub unsafe trait GlobalAlloc { - unsafe fn alloc(&self, layout: Layout) -> *mut Void; + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque; - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout); + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout); - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Void { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { let size = layout.size(); let ptr = self.alloc(layout); if !ptr.is_null() { @@ -404,7 +404,7 @@ pub unsafe trait GlobalAlloc { /// /// `new_size`, when rounded up to the nearest multiple of `old_layout.align()`, /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). - unsafe fn realloc(&self, ptr: *mut Void, old_layout: Layout, new_size: usize) -> *mut Void { + unsafe fn realloc(&self, ptr: *mut Opaque, old_layout: Layout, new_size: usize) -> *mut Opaque { let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); let new_ptr = self.alloc(new_layout); if !new_ptr.is_null() { @@ -554,7 +554,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -571,7 +571,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); /// Allocator-specific method for signaling an out-of-memory /// condition. @@ -689,9 +689,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -732,7 +732,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { @@ -781,7 +781,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -826,7 +826,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -881,7 +881,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -960,7 +960,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - self.dealloc(ptr.as_void(), k); + self.dealloc(ptr.as_opaque(), k); } } @@ -1048,7 +1048,7 @@ pub unsafe trait Alloc { match (Layout::array::(n_old), Layout::array::(n_new)) { (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr.as_void(), k_old.clone(), k_new.size()).map(NonNull::cast) + self.realloc(ptr.as_opaque(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1081,7 +1081,7 @@ pub unsafe trait Alloc { { match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(ptr.as_void(), k.clone())) + Ok(self.dealloc(ptr.as_opaque(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index f4e668328ce..4a7d7c410eb 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2751,9 +2751,9 @@ impl NonNull { } } - /// Cast to a `Void` pointer + /// Cast to an `Opaque` pointer #[unstable(feature = "allocator_api", issue = "32838")] - pub fn as_void(self) -> NonNull<::alloc::Void> { + pub fn as_opaque(self) -> NonNull<::alloc::Opaque> { unsafe { NonNull::new_unchecked(self.as_ptr() as _) } diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 58d4c7f289c..305502e7f06 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -221,7 +221,7 @@ impl<'a> AllocFnFactory<'a> { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); let arg = self.cx.expr_ident(self.span, ident); - self.cx.expr_cast(self.span, arg, self.ptr_void()) + self.cx.expr_cast(self.span, arg, self.ptr_opaque()) } AllocatorTy::Usize => { @@ -276,13 +276,13 @@ impl<'a> AllocFnFactory<'a> { self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - fn ptr_void(&self) -> P { - let void = self.cx.path(self.span, vec![ + fn ptr_opaque(&self) -> P { + let opaque = self.cx.path(self.span, vec![ self.core, Ident::from_str("alloc"), - Ident::from_str("Void"), + Ident::from_str("Opaque"), ]); - let ty_void = self.cx.ty_path(void); - self.cx.ty_ptr(self.span, ty_void, Mutability::Mutable) + let ty_opaque = self.cx.ty_path(opaque); + self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable) } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 4e728df010a..ff578ec42d2 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -21,7 +21,7 @@ #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, GlobalAlloc, Void}; + use super::{System, Layout, GlobalAlloc, Opaque}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -46,7 +46,7 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr as *mut Void, Layout::from_size_align_unchecked(size, align)) + System.dealloc(ptr as *mut Opaque, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] @@ -56,7 +56,7 @@ pub mod __default_lib_allocator { align: usize, new_size: usize) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr as *mut Void, old_layout, new_size) as *mut u8 + System.realloc(ptr as *mut Opaque, old_layout, new_size) as *mut u8 } #[no_mangle] diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 38c99373788..93f059076d7 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1183,7 +1183,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { debug_assert!(!oflo, "should be impossible"); unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_void(), + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), Layout::from_size_align(size, align).unwrap()); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 20b87028fd1..62fd2ade1ca 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -16,11 +16,11 @@ use std::alloc::*; pub struct A; unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, _: Layout) -> *mut Void { + unsafe fn alloc(&self, _: Layout) -> *mut Opaque { loop {} } - unsafe fn dealloc(&self, _ptr: *mut Void, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut Opaque, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 95096efc7ef..e6a2e22983b 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::heap::{GlobalAlloc, System, Layout, Void}; +use std::heap::{GlobalAlloc, System, Layout, Opaque}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index f7b2fd73c87..415d39a593e 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,7 +15,7 @@ extern crate helper; -use std::alloc::{self, Global, Alloc, System, Layout, Void}; +use std::alloc::{self, Global, Alloc, System, Layout, Opaque}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; @@ -23,12 +23,12 @@ static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; unsafe impl alloc::GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Void { + unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Void, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 49ab0ee3310..38cc23c16a9 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Global.dealloc(NonNull::new_unchecked(ptr).as_void(), layout); + Global.dealloc(NonNull::new_unchecked(ptr).as_opaque(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,7 +72,7 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_void(), old.clone(), new.size()) + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) .unwrap_or_else(|_| Global.oom()); if PRINT { -- cgit 1.4.1-3-g733a5 From 66c5e3ffb2b7a0804ceb989b9dc9138a7758bfd6 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Fri, 6 Apr 2018 13:46:10 -0700 Subject: Reduce the size of panics in RawVec Create one canonical location which panics with "capacity overflow" instead of having many. This reduces the size of a `panic!("{}", 1)` binary on wasm from 34k to 17k. --- src/liballoc/raw_vec.rs | 23 +++++++++++++++-------- 1 file changed, 15 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 214cc7d7d0c..ae933f937c4 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -85,8 +85,8 @@ impl RawVec { unsafe { let elem_size = mem::size_of::(); - let alloc_size = cap.checked_mul(elem_size).expect("capacity overflow"); - alloc_guard(alloc_size).expect("capacity overflow"); + let alloc_size = cap.checked_mul(elem_size).unwrap_or_else(|| capacity_overflow()); + alloc_guard(alloc_size).unwrap_or_else(|_| capacity_overflow()); // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { @@ -309,7 +309,7 @@ impl RawVec { // `from_size_align_unchecked`. let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; - alloc_guard(new_size).expect("capacity overflow"); + alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_opaque(), cur, new_size); @@ -368,7 +368,7 @@ impl RawVec { // overflow and the alignment is sufficiently small. let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; - alloc_guard(new_size).expect("capacity overflow"); + alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); match self.a.grow_in_place(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. @@ -440,7 +440,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { - Err(CapacityOverflow) => panic!("capacity overflow"), + Err(CapacityOverflow) => capacity_overflow(), Err(AllocErr) => self.a.oom(), Ok(()) => { /* yay */ } } @@ -550,7 +550,7 @@ impl RawVec { /// The same as try_reserve, but errors are lowered to a call to oom(). pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { - Err(CapacityOverflow) => panic!("capacity overflow"), + Err(CapacityOverflow) => capacity_overflow(), Err(AllocErr) => self.a.oom(), Ok(()) => { /* yay */ } } @@ -591,7 +591,7 @@ impl RawVec { } let new_cap = self.amortized_new_size(used_cap, needed_extra_cap) - .expect("capacity overflow"); + .unwrap_or_else(|_| capacity_overflow()); // Here, `cap < used_cap + needed_extra_cap <= new_cap` // (regardless of whether `self.cap - used_cap` wrapped). @@ -599,7 +599,7 @@ impl RawVec { let new_layout = Layout::new::().repeat(new_cap).unwrap().0; // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size()).expect("capacity overflow"); + alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow()); match self.a.grow_in_place( NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), ) { @@ -731,6 +731,13 @@ fn alloc_guard(alloc_size: usize) -> Result<(), CollectionAllocErr> { } } +// One central function responsible for reporting capacity overflows. This'll +// ensure that the code generation related to these panics is minimal as there's +// only one location which panics rather than a bunch throughout the module. +fn capacity_overflow() -> ! { + panic!("capacity overflow") +} + #[cfg(test)] mod tests { use super::*; -- cgit 1.4.1-3-g733a5 From bd9ff8476d5e8ca5dbb99c70ff2a9dc3be1d59d7 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Sat, 14 Apr 2018 08:13:28 +0900 Subject: Cleanup liballoc use statements Some modules were still using the deprecated `allocator` module, use the `alloc` module instead. Some modules were using `super` while it's not needed. Some modules were more or less ordering them, and other not, so the latter have been modified to match the others. --- src/liballoc/boxed.rs | 4 ++-- src/liballoc/raw_vec.rs | 9 +++++---- src/liballoc/str.rs | 6 +++--- src/liballoc/string.rs | 4 ++-- src/liballoc/vec.rs | 2 +- src/liballoc/vec_deque.rs | 5 ++--- 6 files changed, 15 insertions(+), 15 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 71b53cc88e5..aceb6ff8abe 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -55,8 +55,6 @@ #![stable(feature = "rust1", since = "1.0.0")] -use raw_vec::RawVec; - use core::any::Any; use core::borrow; use core::cmp::Ordering; @@ -68,6 +66,8 @@ use core::mem::{self, Pin}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; use core::convert::From; + +use raw_vec::RawVec; use str::from_boxed_utf8_unchecked; /// A pointer type for heap allocation. diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 214cc7d7d0c..405814c021a 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,15 +8,16 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Alloc, Layout, Global}; use core::cmp; use core::mem; use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use super::boxed::Box; -use super::allocator::CollectionAllocErr; -use super::allocator::CollectionAllocErr::*; + +use alloc::{Alloc, Layout, Global}; +use alloc::CollectionAllocErr; +use alloc::CollectionAllocErr::*; +use boxed::Box; /// A low-level utility for more ergonomically allocating, reallocating, and deallocating /// a buffer of memory on the heap without having to worry about all the corner cases diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 65df93bd3bb..6c9f3dd7ec9 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -46,12 +46,12 @@ use core::mem; use core::ptr; use core::iter::FusedIterator; -use vec_deque::VecDeque; use borrow::{Borrow, ToOwned}; +use boxed::Box; +use slice::{SliceConcatExt, SliceIndex}; use string::String; use vec::Vec; -use slice::{SliceConcatExt, SliceIndex}; -use boxed::Box; +use vec_deque::VecDeque; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{FromStr, Utf8Error}; diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 29d759b1f00..0924ca24791 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -66,11 +66,11 @@ use core::ptr; use core::str::pattern::Pattern; use core::str::lossy; +use alloc::CollectionAllocErr; use borrow::{Cow, ToOwned}; +use boxed::Box; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; use vec::Vec; -use boxed::Box; -use super::allocator::CollectionAllocErr; /// A UTF-8 encoded, growable string. /// diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 0f74743ca49..9ae415c328b 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -82,11 +82,11 @@ use core::ptr; use core::ptr::NonNull; use core::slice; +use alloc::CollectionAllocErr; use borrow::ToOwned; use borrow::Cow; use boxed::Box; use raw_vec::RawVec; -use super::allocator::CollectionAllocErr; /// A contiguous growable array type, written `Vec` but pronounced 'vector'. /// diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index f28c8e38996..603e38ca2ca 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -30,10 +30,9 @@ use core::slice; use core::hash::{Hash, Hasher}; use core::cmp; +use alloc::CollectionAllocErr; use raw_vec::RawVec; - -use super::allocator::CollectionAllocErr; -use super::vec::Vec; +use vec::Vec; const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 const MINIMUM_CAPACITY: usize = 1; // 2 - 1 -- cgit 1.4.1-3-g733a5 From b59fa0d9e81fe36c5d298f8f828aaf0755e96f89 Mon Sep 17 00:00:00 2001 From: Anthony Ramine Date: Sun, 15 Apr 2018 10:34:57 +0200 Subject: Remove #[inline(always)] on Vec::into_boxed_slice --- src/liballoc/vec.rs | 1 - 1 file changed, 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 3fff28469fe..a6498b8861e 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -638,7 +638,6 @@ impl Vec { /// assert_eq!(slice.into_vec().capacity(), 3); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] pub fn into_boxed_slice(mut self) -> Box<[T]> { unsafe { self.shrink_to_fit(); -- cgit 1.4.1-3-g733a5 From 01e8bebaf1964331a5704b5ee65adc216a923f0e Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Tue, 17 Apr 2018 10:23:06 +0900 Subject: Remove no longer necessary comparison to Vec::splice. `String::replace_range` was previously called `String::splice`, so this note was necessary to differentiate it from the `Vec` method. Now that it's renamed, this note no longer seems necessary. --- src/liballoc/string.rs | 3 --- 1 file changed, 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 11fb82c09d3..2f84d5f7f86 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1521,9 +1521,6 @@ impl String { /// and replaces it with the given string. /// The given string doesn't need to be the same length as the range. /// - /// Note: Unlike [`Vec::splice`], the replacement happens eagerly, and this - /// method does not return the removed chars. - /// /// # Panics /// /// Panics if the starting point or end point do not lie on a [`char`] -- cgit 1.4.1-3-g733a5 From edc412c5a9ce825d589dcb87dd6028b072139b51 Mon Sep 17 00:00:00 2001 From: tinaun Date: Tue, 17 Apr 2018 00:30:06 -0400 Subject: stabilize `slice_rsplit` feature --- .../src/library-features/slice-rsplit.md | 10 -------- src/liballoc/lib.rs | 1 - src/liballoc/slice.rs | 11 +++------ src/libcore/slice/mod.rs | 28 +++++++++++----------- 4 files changed, 17 insertions(+), 33 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/slice-rsplit.md (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/library-features/slice-rsplit.md b/src/doc/unstable-book/src/library-features/slice-rsplit.md deleted file mode 100644 index 8c2954f7294..00000000000 --- a/src/doc/unstable-book/src/library-features/slice-rsplit.md +++ /dev/null @@ -1,10 +0,0 @@ -# `slice_rsplit` - -The tracking issue for this feature is: [#41020] - -[#41020]: https://github.com/rust-lang/rust/issues/41020 - ------------------------- - -The `slice_rsplit` feature enables two methods on slices: -`slice.rsplit(predicate)` and `slice.rsplit_mut(predicate)`. diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 3a106a2ff5c..87ad2751c5b 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -108,7 +108,6 @@ #![feature(ptr_offset_from)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] -#![feature(slice_rsplit)] #![feature(specialization)] #![feature(staged_api)] #![feature(str_internals)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 56c53fca62c..eb8a293013d 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -116,7 +116,7 @@ pub use core::slice::{Iter, IterMut}; pub use core::slice::{SplitMut, ChunksMut, Split}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{SplitN, RSplitN, SplitNMut, RSplitNMut}; -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] pub use core::slice::{RSplit, RSplitMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{from_raw_parts, from_raw_parts_mut}; @@ -888,7 +888,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_rsplit)] /// /// let slice = [11, 22, 33, 0, 44, 55]; /// let mut iter = slice.rsplit(|num| *num == 0); @@ -902,8 +901,6 @@ impl [T] { /// slice will be the first (or last) item returned by the iterator. /// /// ``` - /// #![feature(slice_rsplit)] - /// /// let v = &[0, 1, 1, 2, 3, 5, 8]; /// let mut it = v.rsplit(|n| *n % 2 == 0); /// assert_eq!(it.next().unwrap(), &[]); @@ -912,7 +909,7 @@ impl [T] { /// assert_eq!(it.next().unwrap(), &[]); /// assert_eq!(it.next(), None); /// ``` - #[unstable(feature = "slice_rsplit", issue = "41020")] + #[stable(feature = "slice_rsplit", since = "1.27.0")] #[inline] pub fn rsplit(&self, pred: F) -> RSplit where F: FnMut(&T) -> bool @@ -927,8 +924,6 @@ impl [T] { /// # Examples /// /// ``` - /// #![feature(slice_rsplit)] - /// /// let mut v = [100, 400, 300, 200, 600, 500]; /// /// let mut count = 0; @@ -939,7 +934,7 @@ impl [T] { /// assert_eq!(v, [3, 400, 300, 2, 600, 1]); /// ``` /// - #[unstable(feature = "slice_rsplit", issue = "41020")] + #[stable(feature = "slice_rsplit", since = "1.27.0")] #[inline] pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut where F: FnMut(&T) -> bool diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 0a22028da81..68f081c2e87 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -86,7 +86,7 @@ pub trait SliceExt { fn split

(&self, pred: P) -> Split where P: FnMut(&Self::Item) -> bool; - #[unstable(feature = "slice_rsplit", issue = "41020")] + #[stable(feature = "slice_rsplit", since = "1.27.0")] fn rsplit

(&self, pred: P) -> RSplit where P: FnMut(&Self::Item) -> bool; @@ -169,7 +169,7 @@ pub trait SliceExt { fn split_mut

(&mut self, pred: P) -> SplitMut where P: FnMut(&Self::Item) -> bool; - #[unstable(feature = "slice_rsplit", issue = "41020")] + #[stable(feature = "slice_rsplit", since = "1.27.0")] fn rsplit_mut

(&mut self, pred: P) -> RSplitMut where P: FnMut(&Self::Item) -> bool; @@ -1840,13 +1840,13 @@ impl<'a, T, P> FusedIterator for SplitMut<'a, T, P> where P: FnMut(&T) -> bool { /// /// [`rsplit`]: ../../std/primitive.slice.html#method.rsplit /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] #[derive(Clone)] // Is this correct, or does it incorrectly require `T: Clone`? pub struct RSplit<'a, T:'a, P> where P: FnMut(&T) -> bool { inner: Split<'a, T, P> } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for RSplit<'a, T, P> where P: FnMut(&T) -> bool { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RSplit") @@ -1856,7 +1856,7 @@ impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for RSplit<'a, T, P> where P: FnMut(& } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> Iterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool { type Item = &'a [T]; @@ -1871,7 +1871,7 @@ impl<'a, T, P> Iterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool { } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> DoubleEndedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool { #[inline] fn next_back(&mut self) -> Option<&'a [T]> { @@ -1879,7 +1879,7 @@ impl<'a, T, P> DoubleEndedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bo } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> SplitIter for RSplit<'a, T, P> where P: FnMut(&T) -> bool { #[inline] fn finish(&mut self) -> Option<&'a [T]> { @@ -1887,7 +1887,7 @@ impl<'a, T, P> SplitIter for RSplit<'a, T, P> where P: FnMut(&T) -> bool { } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> FusedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool {} /// An iterator over the subslices of the vector which are separated @@ -1897,12 +1897,12 @@ impl<'a, T, P> FusedIterator for RSplit<'a, T, P> where P: FnMut(&T) -> bool {} /// /// [`rsplit_mut`]: ../../std/primitive.slice.html#method.rsplit_mut /// [slices]: ../../std/primitive.slice.html -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] pub struct RSplitMut<'a, T:'a, P> where P: FnMut(&T) -> bool { inner: SplitMut<'a, T, P> } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.debug_struct("RSplitMut") @@ -1912,7 +1912,7 @@ impl<'a, T: 'a + fmt::Debug, P> fmt::Debug for RSplitMut<'a, T, P> where P: FnMu } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> SplitIter for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool { #[inline] fn finish(&mut self) -> Option<&'a mut [T]> { @@ -1920,7 +1920,7 @@ impl<'a, T, P> SplitIter for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool { } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> Iterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool { type Item = &'a mut [T]; @@ -1935,7 +1935,7 @@ impl<'a, T, P> Iterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool { } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool, { @@ -1945,7 +1945,7 @@ impl<'a, T, P> DoubleEndedIterator for RSplitMut<'a, T, P> where } } -#[unstable(feature = "slice_rsplit", issue = "41020")] +#[stable(feature = "slice_rsplit", since = "1.27.0")] impl<'a, T, P> FusedIterator for RSplitMut<'a, T, P> where P: FnMut(&T) -> bool {} /// An private iterator over subslices separated by elements that -- cgit 1.4.1-3-g733a5 From 78a8c257032b18b5ca63f41b18d2fe7d57d1cffa Mon Sep 17 00:00:00 2001 From: tinaun Date: Tue, 17 Apr 2018 00:40:07 -0400 Subject: stabilize `swap_with_slice` feature --- src/liballoc/lib.rs | 2 +- src/liballoc/slice.rs | 8 +------- src/libcore/slice/mod.rs | 2 +- 3 files changed, 3 insertions(+), 9 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 87ad2751c5b..f2a61bda4aa 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -123,7 +123,7 @@ #![feature(inclusive_range_fields)] #![cfg_attr(stage0, feature(generic_param_attrs))] -#![cfg_attr(not(test), feature(fn_traits, swap_with_slice, i128))] +#![cfg_attr(not(test), feature(fn_traits, i128))] #![cfg_attr(test, feature(test))] // Allow testing this library diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index eb8a293013d..33e652856e8 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1702,8 +1702,6 @@ impl [T] { /// Swapping two elements across slices: /// /// ``` - /// #![feature(swap_with_slice)] - /// /// let mut slice1 = [0, 0]; /// let mut slice2 = [1, 2, 3, 4]; /// @@ -1719,8 +1717,6 @@ impl [T] { /// a compile failure: /// /// ```compile_fail - /// #![feature(swap_with_slice)] - /// /// let mut slice = [1, 2, 3, 4, 5]; /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! /// ``` @@ -1729,8 +1725,6 @@ impl [T] { /// mutable sub-slices from a slice: /// /// ``` - /// #![feature(swap_with_slice)] - /// /// let mut slice = [1, 2, 3, 4, 5]; /// /// { @@ -1742,7 +1736,7 @@ impl [T] { /// ``` /// /// [`split_at_mut`]: #method.split_at_mut - #[unstable(feature = "swap_with_slice", issue = "44030")] + #[stable(feature = "swap_with_slice", since = "1.27.0")] pub fn swap_with_slice(&mut self, other: &mut [T]) { core_slice::SliceExt::swap_with_slice(self, other) } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 68f081c2e87..afb149f2997 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -223,7 +223,7 @@ pub trait SliceExt { #[stable(feature = "copy_from_slice", since = "1.9.0")] fn copy_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Copy; - #[unstable(feature = "swap_with_slice", issue = "44030")] + #[stable(feature = "swap_with_slice", since = "1.27.0")] fn swap_with_slice(&mut self, src: &mut [Self::Item]); #[stable(feature = "sort_unstable", since = "1.20.0")] -- cgit 1.4.1-3-g733a5 From b84baf23788e96a1d79de543eb264ff7d2334c63 Mon Sep 17 00:00:00 2001 From: tinaun Date: Tue, 17 Apr 2018 00:59:16 -0400 Subject: stabilize `nonnull_cast` feature --- src/liballoc/lib.rs | 1 - src/libcore/ptr.rs | 2 +- src/libstd/lib.rs | 1 - src/test/run-pass/realloc-16687.rs | 2 +- 4 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f2a61bda4aa..163aef61b43 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -99,7 +99,6 @@ #![feature(lang_items)] #![feature(libc)] #![feature(needs_allocator)] -#![feature(nonnull_cast)] #![feature(nonzero)] #![feature(optin_builtin_traits)] #![feature(pattern)] diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index f953b29fdc8..74bb264cc67 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2742,7 +2742,7 @@ impl NonNull { } /// Cast to a pointer of another type - #[unstable(feature = "nonnull_cast", issue = "47653")] + #[stable(feature = "nonnull_cast", since = "1.27.0")] pub fn cast(self) -> NonNull { unsafe { NonNull::new_unchecked(self.as_ptr() as *mut U) diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index dd96c57538c..63e4a17d32e 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -275,7 +275,6 @@ #![feature(macro_reexport)] #![feature(macro_vis_matcher)] #![feature(needs_panic_runtime)] -#![feature(nonnull_cast)] #![feature(exhaustive_patterns)] #![feature(nonzero)] #![feature(num_bits_bytes)] diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 38cc23c16a9..afa3494c389 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -13,7 +13,7 @@ // Ideally this would be revised to use no_std, but for now it serves // well enough to reproduce (and illustrate) the bug from #16687. -#![feature(heap_api, allocator_api, nonnull_cast)] +#![feature(heap_api, allocator_api)] use std::alloc::{Global, Alloc, Layout}; use std::ptr::{self, NonNull}; -- cgit 1.4.1-3-g733a5 From b74d6922ff15d9f3bf39d317ccd7141518f4f5ec Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 16 Apr 2018 16:34:09 -0400 Subject: smaller PR just to fix #50002 --- src/liballoc/tests/str.rs | 16 ++++++++++++++++ src/libcore/str/mod.rs | 9 ++------- 2 files changed, 18 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a3f4c385fe2..e3f198f7362 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -401,6 +401,22 @@ fn test_str_get_maxinclusive() { } } +#[test] +fn test_str_slicemut_rangetoinclusive_ok() { + let mut s = "abcαβγ".to_owned(); + let s: &mut str = &mut s; + &mut s[..=3]; // before alpha + &mut s[..=5]; // after alpha +} + +#[test] +#[should_panic] +fn test_str_slicemut_rangetoinclusive_notok() { + let mut s = "abcαβγ".to_owned(); + let s: &mut str = &mut s; + &mut s[..=4]; // middle of alpha, which is 2 bytes long +} + #[test] fn test_is_char_boundary() { let s = "ศไทย中华Việt Nam β-release 🐱123"; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f1fe23092de..5b52119d031 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2100,18 +2100,13 @@ mod traits { fn index(self, slice: &str) -> &Self::Output { assert!(self.end != usize::max_value(), "attempted to index str up to maximum usize"); - let end = self.end + 1; - self.get(slice).unwrap_or_else(|| super::slice_error_fail(slice, 0, end)) + (..self.end+1).index(slice) } #[inline] fn index_mut(self, slice: &mut str) -> &mut Self::Output { assert!(self.end != usize::max_value(), "attempted to index str up to maximum usize"); - if slice.is_char_boundary(self.end) { - unsafe { self.get_unchecked_mut(slice) } - } else { - super::slice_error_fail(slice, 0, self.end + 1) - } + (..self.end+1).index_mut(slice) } } -- cgit 1.4.1-3-g733a5 From 90b361b3a748e9fb01cd9aec7b83edca2d9e996e Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Wed, 18 Apr 2018 16:48:34 -0400 Subject: fix my unit test that was horrendously wrong and add one for non-mut slicing since I touched that method too --- src/liballoc/tests/str.rs | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index e3f198f7362..a03b61ec97e 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -401,12 +401,26 @@ fn test_str_get_maxinclusive() { } } +#[test] +fn test_str_slice_rangetoinclusive_ok() { + let s = "abcαβγ"; + assert_eq!(&s[..=2], "abc"); + assert_eq!(&s[..=4], "abcα"); +} + +#[test] +#[should_panic] +fn test_str_slice_rangetoinclusive_notok() { + let s = "abcαβγ"; + &s[..=3]; +} + #[test] fn test_str_slicemut_rangetoinclusive_ok() { let mut s = "abcαβγ".to_owned(); let s: &mut str = &mut s; - &mut s[..=3]; // before alpha - &mut s[..=5]; // after alpha + assert_eq!(&mut s[..=2], "abc"); + assert_eq!(&mut s[..=4], "abcα"); } #[test] @@ -414,7 +428,7 @@ fn test_str_slicemut_rangetoinclusive_ok() { fn test_str_slicemut_rangetoinclusive_notok() { let mut s = "abcαβγ".to_owned(); let s: &mut str = &mut s; - &mut s[..=4]; // middle of alpha, which is 2 bytes long + &mut s[..=3]; } #[test] -- cgit 1.4.1-3-g733a5 From ca79ba300a0934864d6ea520f9424d5f08ece687 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 19 Apr 2018 15:52:14 -0700 Subject: Tweak some stabilizations in libstd This commit tweaks a few stable APIs in the `beta` branch before they hit stable. The `str::is_whitespace` and `str::is_alphanumeric` functions were deleted (added in #49381, issue at #49657). The `and_modify` APIs added in #44734 were altered to take a `FnOnce` closure rather than a `FnMut` closure. Closes #49581 Closes #49657 --- src/liballoc/btree/map.rs | 4 ++-- src/liballoc/str.rs | 42 -------------------------------------- src/librustdoc/test.rs | 2 +- src/libstd/collections/hash/map.rs | 4 ++-- 4 files changed, 5 insertions(+), 47 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 82cbec0517e..3984379ea86 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2155,8 +2155,8 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 43); /// ``` #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, mut f: F) -> Self - where F: FnMut(&mut V) + pub fn and_modify(self, f: F) -> Self + where F: FnOnce(&mut V) { match self { Occupied(mut entry) => { diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 0e708465332..686a0408a7c 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -2140,48 +2140,6 @@ impl str { unsafe { String::from_utf8_unchecked(buf) } } - /// Returns true if this `str` is entirely whitespace, and false otherwise. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived Core - /// Property `White_Space`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert!(" \t ".is_whitespace()); - /// - /// // a non-breaking space - /// assert!("\u{A0}".is_whitespace()); - /// - /// assert!(!" 越".is_whitespace()); - /// ``` - #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] - #[inline] - pub fn is_whitespace(&self) -> bool { - StrExt::is_whitespace(self) - } - - /// Returns true if this `str` is entirely alphanumeric, and false otherwise. - /// - /// 'Alphanumeric'-ness is defined in terms of the Unicode General Categories - /// 'Nd', 'Nl', 'No' and the Derived Core Property 'Alphabetic'. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert!("٣7৬Kو藏".is_alphanumeric()); - /// assert!(!"¾①".is_alphanumeric()); - /// ``` - #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] - #[inline] - pub fn is_alphanumeric(&self) -> bool { - StrExt::is_alphanumeric(self) - } - /// Checks if all characters in this string are within the ASCII range. /// /// # Examples diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 600e9eaa05f..8cd5f373efe 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -437,7 +437,7 @@ fn partition_source(s: &str) -> (String, String) { for line in s.lines() { let trimline = line.trim(); - let header = trimline.is_whitespace() || + let header = trimline.chars().all(|c| c.is_whitespace()) || trimline.starts_with("#![") || trimline.starts_with("#[macro_use] extern crate") || trimline.starts_with("extern crate"); diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 20a4f9b508d..64590fc0d10 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2127,8 +2127,8 @@ impl<'a, K, V> Entry<'a, K, V> { /// assert_eq!(map["poneyland"], 43); /// ``` #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, mut f: F) -> Self - where F: FnMut(&mut V) + pub fn and_modify(self, f: F) -> Self + where F: FnOnce(&mut V) { match self { Occupied(mut entry) => { -- cgit 1.4.1-3-g733a5 From de8ed6a1d6ae3b2f2c7f1035ef3b71abda7a6a84 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Apr 2018 11:45:22 +0200 Subject: Move non-allocating [u8] inherent methods to libcore Fixes #45803 --- src/liballoc/slice.rs | 58 ++-------------------- src/libcore/lib.rs | 1 + src/libcore/slice/mod.rs | 65 +++++++++++++++++++++++++ src/librustc/middle/lang_items.rs | 1 + src/librustc_typeck/check/method/probe.rs | 3 ++ src/librustc_typeck/coherence/inherent_impls.rs | 28 ++++++++++- src/librustdoc/clean/inline.rs | 1 + 7 files changed, 101 insertions(+), 56 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 33e652856e8..70945a791f6 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -1782,16 +1782,10 @@ impl [T] { } } -#[lang = "slice_u8"] +#[cfg_attr(stage0, lang = "slice_u8")] +#[cfg_attr(not(stage0), lang = "slice_u8_alloc")] #[cfg(not(test))] impl [u8] { - /// Checks if all bytes in this slice are within the ASCII range. - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn is_ascii(&self) -> bool { - self.iter().all(|b| b.is_ascii()) - } - /// Returns a vector containing a copy of this slice where each byte /// is mapped to its ASCII upper case equivalent. /// @@ -1826,52 +1820,8 @@ impl [u8] { me } - /// Checks that two slices are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { - self.len() == other.len() && - self.iter().zip(other).all(|(a, b)| { - a.eq_ignore_ascii_case(b) - }) - } - - /// Converts this slice to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_uppercase(&mut self) { - for byte in self { - byte.make_ascii_uppercase(); - } - } - - /// Converts this slice to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn make_ascii_lowercase(&mut self) { - for byte in self { - byte.make_ascii_lowercase(); - } - } + #[cfg(stage0)] + slice_u8_core_methods!(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index ea7a46f44ae..88bd0444233 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -92,6 +92,7 @@ #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(simd_ffi)] +#![feature(core_slice_ext)] #![feature(specialization)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index afb149f2997..4cda1c8778a 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -755,6 +755,71 @@ impl SliceExt for [T] { } } +#[cfg_attr(stage0, macro_export)] +#[unstable(feature = "core_slice_ext", issue = "32110")] +macro_rules! slice_u8_core_methods { () => { + /// Checks if all bytes in this slice are within the ASCII range. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn is_ascii(&self) -> bool { + self.iter().all(|b| b.is_ascii()) + } + + /// Checks that two slices are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &[u8]) -> bool { + self.len() == other.len() && + self.iter().zip(other).all(|(a, b)| { + a.eq_ignore_ascii_case(b) + }) + } + + /// Converts this slice to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_uppercase(&mut self) { + for byte in self { + byte.make_ascii_uppercase(); + } + } + + /// Converts this slice to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn make_ascii_lowercase(&mut self) { + for byte in self { + byte.make_ascii_lowercase(); + } + } +}} + +#[lang = "slice_u8"] +#[cfg(not(test))] +#[cfg(not(stage0))] +impl [u8] { + slice_u8_core_methods!(); +} + #[stable(feature = "rust1", since = "1.0.0")] #[rustc_on_unimplemented = "slice indices are of type `usize` or ranges of `usize`"] impl ops::Index for [T] diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 3b37031cf46..f8cff8b186c 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -214,6 +214,7 @@ language_item_table! { StrImplItem, "str", str_impl; SliceImplItem, "slice", slice_impl; SliceU8ImplItem, "slice_u8", slice_u8_impl; + SliceU8AllocImplItem, "slice_u8_alloc", slice_u8_alloc_impl; ConstPtrImplItem, "const_ptr", const_ptr_impl; MutPtrImplItem, "mut_ptr", mut_ptr_impl; I8ImplItem, "i8", i8_impl; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index de570956622..7ba60f62791 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -478,6 +478,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let lang_def_id = lang_items.slice_u8_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + + let lang_def_id = lang_items.slice_u8_alloc_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { let lang_def_id = lang_items.const_ptr_impl(); diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index d43ab0d3713..d7657bae8c5 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -114,6 +114,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyChar => { self.check_primitive_impl(def_id, lang_items.char_impl(), + None, "char", "char", item.span); @@ -121,6 +122,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyStr => { self.check_primitive_impl(def_id, lang_items.str_impl(), + None, "str", "str", item.span); @@ -128,6 +130,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TySlice(slice_item) if slice_item == self.tcx.types.u8 => { self.check_primitive_impl(def_id, lang_items.slice_u8_impl(), + lang_items.slice_u8_alloc_impl(), "slice_u8", "[u8]", item.span); @@ -135,6 +138,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TySlice(_) => { self.check_primitive_impl(def_id, lang_items.slice_impl(), + None, "slice", "[T]", item.span); @@ -142,6 +146,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: hir::MutImmutable }) => { self.check_primitive_impl(def_id, lang_items.const_ptr_impl(), + None, "const_ptr", "*const T", item.span); @@ -149,6 +154,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyRawPtr(ty::TypeAndMut { ty: _, mutbl: hir::MutMutable }) => { self.check_primitive_impl(def_id, lang_items.mut_ptr_impl(), + None, "mut_ptr", "*mut T", item.span); @@ -156,6 +162,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::I8) => { self.check_primitive_impl(def_id, lang_items.i8_impl(), + None, "i8", "i8", item.span); @@ -163,6 +170,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::I16) => { self.check_primitive_impl(def_id, lang_items.i16_impl(), + None, "i16", "i16", item.span); @@ -170,6 +178,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::I32) => { self.check_primitive_impl(def_id, lang_items.i32_impl(), + None, "i32", "i32", item.span); @@ -177,6 +186,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::I64) => { self.check_primitive_impl(def_id, lang_items.i64_impl(), + None, "i64", "i64", item.span); @@ -184,6 +194,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::I128) => { self.check_primitive_impl(def_id, lang_items.i128_impl(), + None, "i128", "i128", item.span); @@ -191,6 +202,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyInt(ast::IntTy::Isize) => { self.check_primitive_impl(def_id, lang_items.isize_impl(), + None, "isize", "isize", item.span); @@ -198,6 +210,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::U8) => { self.check_primitive_impl(def_id, lang_items.u8_impl(), + None, "u8", "u8", item.span); @@ -205,6 +218,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::U16) => { self.check_primitive_impl(def_id, lang_items.u16_impl(), + None, "u16", "u16", item.span); @@ -212,6 +226,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::U32) => { self.check_primitive_impl(def_id, lang_items.u32_impl(), + None, "u32", "u32", item.span); @@ -219,6 +234,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::U64) => { self.check_primitive_impl(def_id, lang_items.u64_impl(), + None, "u64", "u64", item.span); @@ -226,6 +242,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::U128) => { self.check_primitive_impl(def_id, lang_items.u128_impl(), + None, "u128", "u128", item.span); @@ -233,6 +250,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyUint(ast::UintTy::Usize) => { self.check_primitive_impl(def_id, lang_items.usize_impl(), + None, "usize", "usize", item.span); @@ -240,6 +258,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyFloat(ast::FloatTy::F32) => { self.check_primitive_impl(def_id, lang_items.f32_impl(), + None, "f32", "f32", item.span); @@ -247,6 +266,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyFloat(ast::FloatTy::F64) => { self.check_primitive_impl(def_id, lang_items.f64_impl(), + None, "f64", "f64", item.span); @@ -305,11 +325,15 @@ impl<'a, 'tcx> InherentCollect<'a, 'tcx> { fn check_primitive_impl(&self, impl_def_id: DefId, lang_def_id: Option, + lang_def_id2: Option, lang: &str, ty: &str, span: Span) { - match lang_def_id { - Some(lang_def_id) if lang_def_id == impl_def_id => { + match (lang_def_id, lang_def_id2) { + (Some(lang_def_id), _) if lang_def_id == impl_def_id => { + // OK + } + (_, Some(lang_def_id)) if lang_def_id == impl_def_id => { // OK } _ => { diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 32f23e923d9..4acd12e5862 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -290,6 +290,7 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec Date: Sat, 7 Apr 2018 19:38:35 +0200 Subject: Replace SliceExt with inherent [T] methods in libcore --- src/liballoc/lib.rs | 1 + src/liballoc/slice.rs | 1396 +--------------------- src/libcore/lib.rs | 2 +- src/libcore/prelude/v1.rs | 1 + src/libcore/slice/mod.rs | 1404 +++++++++++++++++++++++ src/librustc/middle/lang_items.rs | 1 + src/librustc_typeck/check/method/probe.rs | 3 + src/librustc_typeck/coherence/inherent_impls.rs | 2 +- src/librustdoc/clean/inline.rs | 1 + 9 files changed, 1418 insertions(+), 1393 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 163aef61b43..52011303543 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -90,6 +90,7 @@ #![feature(collections_range)] #![feature(const_fn)] #![feature(core_intrinsics)] +#![cfg_attr(stage0, feature(core_slice_ext))] #![feature(custom_attribute)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 70945a791f6..4594263c01f 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -101,7 +101,7 @@ use core::cmp::Ordering::{self, Less}; use core::mem::size_of; use core::mem; use core::ptr; -use core::slice as core_slice; +#[cfg(stage0)] use core::slice::SliceExt; use core::{u8, u16, u32}; use borrow::{Borrow, BorrowMut, ToOwned}; @@ -171,1059 +171,12 @@ mod hack { } } -#[lang = "slice"] +#[cfg_attr(stage0, lang = "slice")] +#[cfg_attr(not(stage0), lang = "slice_alloc")] #[cfg(not(test))] impl [T] { - /// Returns the number of elements in the slice. - /// - /// # Examples - /// - /// ``` - /// let a = [1, 2, 3]; - /// assert_eq!(a.len(), 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn len(&self) -> usize { - core_slice::SliceExt::len(self) - } - - /// Returns `true` if the slice has a length of 0. - /// - /// # Examples - /// - /// ``` - /// let a = [1, 2, 3]; - /// assert!(!a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_empty(&self) -> bool { - core_slice::SliceExt::is_empty(self) - } - - /// Returns the first element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&10), v.first()); - /// - /// let w: &[i32] = &[]; - /// assert_eq!(None, w.first()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn first(&self) -> Option<&T> { - core_slice::SliceExt::first(self) - } - - /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(first) = x.first_mut() { - /// *first = 5; - /// } - /// assert_eq!(x, &[5, 1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn first_mut(&mut self) -> Option<&mut T> { - core_slice::SliceExt::first_mut(self) - } - - /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &[0, 1, 2]; - /// - /// if let Some((first, elements)) = x.split_first() { - /// assert_eq!(first, &0); - /// assert_eq!(elements, &[1, 2]); - /// } - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_first(&self) -> Option<(&T, &[T])> { - core_slice::SliceExt::split_first(self) - } - - /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some((first, elements)) = x.split_first_mut() { - /// *first = 3; - /// elements[0] = 4; - /// elements[1] = 5; - /// } - /// assert_eq!(x, &[3, 4, 5]); - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - core_slice::SliceExt::split_first_mut(self) - } - - /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &[0, 1, 2]; - /// - /// if let Some((last, elements)) = x.split_last() { - /// assert_eq!(last, &2); - /// assert_eq!(elements, &[0, 1]); - /// } - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_last(&self) -> Option<(&T, &[T])> { - core_slice::SliceExt::split_last(self) - - } - - /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some((last, elements)) = x.split_last_mut() { - /// *last = 3; - /// elements[0] = 4; - /// elements[1] = 5; - /// } - /// assert_eq!(x, &[4, 5, 3]); - /// ``` - #[stable(feature = "slice_splits", since = "1.5.0")] - #[inline] - pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - core_slice::SliceExt::split_last_mut(self) - } - - /// Returns the last element of the slice, or `None` if it is empty. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&30), v.last()); - /// - /// let w: &[i32] = &[]; - /// assert_eq!(None, w.last()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn last(&self) -> Option<&T> { - core_slice::SliceExt::last(self) - } - - /// Returns a mutable pointer to the last item in the slice. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(last) = x.last_mut() { - /// *last = 10; - /// } - /// assert_eq!(x, &[0, 1, 10]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn last_mut(&mut self) -> Option<&mut T> { - core_slice::SliceExt::last_mut(self) - } - - /// Returns a reference to an element or subslice depending on the type of - /// index. - /// - /// - If given a position, returns a reference to the element at that - /// position or `None` if out of bounds. - /// - If given a range, returns the subslice corresponding to that range, - /// or `None` if out of bounds. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert_eq!(Some(&40), v.get(1)); - /// assert_eq!(Some(&[10, 40][..]), v.get(0..2)); - /// assert_eq!(None, v.get(3)); - /// assert_eq!(None, v.get(0..4)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get(&self, index: I) -> Option<&I::Output> - where I: SliceIndex - { - core_slice::SliceExt::get(self, index) - } - - /// Returns a mutable reference to an element or subslice depending on the - /// type of index (see [`get`]) or `None` if the index is out of bounds. - /// - /// [`get`]: #method.get - /// - /// # Examples - /// - /// ``` - /// let x = &mut [0, 1, 2]; - /// - /// if let Some(elem) = x.get_mut(1) { - /// *elem = 42; - /// } - /// assert_eq!(x, &[0, 42, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex - { - core_slice::SliceExt::get_mut(self, index) - } - - /// Returns a reference to an element or subslice, without doing bounds - /// checking. - /// - /// This is generally not recommended, use with caution! For a safe - /// alternative see [`get`]. - /// - /// [`get`]: #method.get - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// - /// unsafe { - /// assert_eq!(x.get_unchecked(1), &2); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub unsafe fn get_unchecked(&self, index: I) -> &I::Output - where I: SliceIndex - { - core_slice::SliceExt::get_unchecked(self, index) - } - - /// Returns a mutable reference to an element or subslice, without doing - /// bounds checking. - /// - /// This is generally not recommended, use with caution! For a safe - /// alternative see [`get_mut`]. - /// - /// [`get_mut`]: #method.get_mut - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// - /// unsafe { - /// let elem = x.get_unchecked_mut(1); - /// *elem = 13; - /// } - /// assert_eq!(x, &[1, 13, 4]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where I: SliceIndex - { - core_slice::SliceExt::get_unchecked_mut(self, index) - } - - /// Returns a raw pointer to the slice's buffer. - /// - /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. - /// - /// Modifying the container referenced by this slice may cause its buffer - /// to be reallocated, which would also make any pointers to it invalid. - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// let x_ptr = x.as_ptr(); - /// - /// unsafe { - /// for i in 0..x.len() { - /// assert_eq!(x.get_unchecked(i), &*x_ptr.offset(i as isize)); - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn as_ptr(&self) -> *const T { - core_slice::SliceExt::as_ptr(self) - } - - /// Returns an unsafe mutable pointer to the slice's buffer. - /// - /// The caller must ensure that the slice outlives the pointer this - /// function returns, or else it will end up pointing to garbage. - /// - /// Modifying the container referenced by this slice may cause its buffer - /// to be reallocated, which would also make any pointers to it invalid. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// let x_ptr = x.as_mut_ptr(); - /// - /// unsafe { - /// for i in 0..x.len() { - /// *x_ptr.offset(i as isize) += 2; - /// } - /// } - /// assert_eq!(x, &[3, 4, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn as_mut_ptr(&mut self) -> *mut T { - core_slice::SliceExt::as_mut_ptr(self) - } - - /// Swaps two elements in the slice. - /// - /// # Arguments - /// - /// * a - The index of the first element - /// * b - The index of the second element - /// - /// # Panics - /// - /// Panics if `a` or `b` are out of bounds. - /// - /// # Examples - /// - /// ``` - /// let mut v = ["a", "b", "c", "d"]; - /// v.swap(1, 3); - /// assert!(v == ["a", "d", "c", "b"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn swap(&mut self, a: usize, b: usize) { - core_slice::SliceExt::swap(self, a, b) - } - - /// Reverses the order of elements in the slice, in place. - /// - /// # Examples - /// - /// ``` - /// let mut v = [1, 2, 3]; - /// v.reverse(); - /// assert!(v == [3, 2, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn reverse(&mut self) { - core_slice::SliceExt::reverse(self) - } - - /// Returns an iterator over the slice. - /// - /// # Examples - /// - /// ``` - /// let x = &[1, 2, 4]; - /// let mut iterator = x.iter(); - /// - /// assert_eq!(iterator.next(), Some(&1)); - /// assert_eq!(iterator.next(), Some(&2)); - /// assert_eq!(iterator.next(), Some(&4)); - /// assert_eq!(iterator.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn iter(&self) -> Iter { - core_slice::SliceExt::iter(self) - } - - /// Returns an iterator that allows modifying each value. - /// - /// # Examples - /// - /// ``` - /// let x = &mut [1, 2, 4]; - /// for elem in x.iter_mut() { - /// *elem += 2; - /// } - /// assert_eq!(x, &[3, 4, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn iter_mut(&mut self) -> IterMut { - core_slice::SliceExt::iter_mut(self) - } - - /// Returns an iterator over all contiguous windows of length - /// `size`. The windows overlap. If the slice is shorter than - /// `size`, the iterator returns no values. - /// - /// # Panics - /// - /// Panics if `size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['r', 'u', 's', 't']; - /// let mut iter = slice.windows(2); - /// assert_eq!(iter.next().unwrap(), &['r', 'u']); - /// assert_eq!(iter.next().unwrap(), &['u', 's']); - /// assert_eq!(iter.next().unwrap(), &['s', 't']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If the slice is shorter than `size`: - /// - /// ``` - /// let slice = ['f', 'o', 'o']; - /// let mut iter = slice.windows(4); - /// assert!(iter.next().is_none()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn windows(&self, size: usize) -> Windows { - core_slice::SliceExt::windows(self, size) - } - - /// Returns an iterator over `chunk_size` elements of the slice at a - /// time. The chunks are slices and do not overlap. If `chunk_size` does - /// not divide the length of the slice, then the last chunk will - /// not have length `chunk_size`. - /// - /// See [`exact_chunks`] for a variant of this iterator that returns chunks - /// of always exactly `chunk_size` elements. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.chunks(2); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert_eq!(iter.next().unwrap(), &['m']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// [`exact_chunks`]: #method.exact_chunks - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chunks(&self, chunk_size: usize) -> Chunks { - core_slice::SliceExt::chunks(self, chunk_size) - } - - /// Returns an iterator over `chunk_size` elements of the slice at a - /// time. The chunks are slices and do not overlap. If `chunk_size` does - /// not divide the length of the slice, then the last up to `chunk_size-1` - /// elements will be omitted. - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler - /// can often optimize the resulting code better than in the case of - /// [`chunks`]. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// #![feature(exact_chunks)] - /// - /// let slice = ['l', 'o', 'r', 'e', 'm']; - /// let mut iter = slice.exact_chunks(2); - /// assert_eq!(iter.next().unwrap(), &['l', 'o']); - /// assert_eq!(iter.next().unwrap(), &['r', 'e']); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// [`chunks`]: #method.chunks - #[unstable(feature = "exact_chunks", issue = "47115")] - #[inline] - pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { - core_slice::SliceExt::exact_chunks(self, chunk_size) - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time. - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does - /// not divide the length of the slice, then the last chunk will not - /// have length `chunk_size`. - /// - /// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks - /// of always exactly `chunk_size` elements. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.chunks_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 3]); - /// ``` - /// - /// [`exact_chunks_mut`]: #method.exact_chunks_mut - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { - core_slice::SliceExt::chunks_mut(self, chunk_size) - } - - /// Returns an iterator over `chunk_size` elements of the slice at a time. - /// The chunks are mutable slices, and do not overlap. If `chunk_size` does - /// not divide the length of the slice, then the last up to `chunk_size-1` - /// elements will be omitted. - /// - /// - /// Due to each chunk having exactly `chunk_size` elements, the compiler - /// can often optimize the resulting code better than in the case of - /// [`chunks_mut`]. - /// - /// # Panics - /// - /// Panics if `chunk_size` is 0. - /// - /// # Examples - /// - /// ``` - /// #![feature(exact_chunks)] - /// - /// let v = &mut [0, 0, 0, 0, 0]; - /// let mut count = 1; - /// - /// for chunk in v.exact_chunks_mut(2) { - /// for elem in chunk.iter_mut() { - /// *elem += count; - /// } - /// count += 1; - /// } - /// assert_eq!(v, &[1, 1, 2, 2, 0]); - /// ``` - /// - /// [`chunks_mut`]: #method.chunks_mut - #[unstable(feature = "exact_chunks", issue = "47115")] - #[inline] - pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { - core_slice::SliceExt::exact_chunks_mut(self, chunk_size) - } - - /// Divides one slice into two at an index. - /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `mid > len`. - /// - /// # Examples - /// - /// ``` - /// let v = [1, 2, 3, 4, 5, 6]; - /// - /// { - /// let (left, right) = v.split_at(0); - /// assert!(left == []); - /// assert!(right == [1, 2, 3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_at(2); - /// assert!(left == [1, 2]); - /// assert!(right == [3, 4, 5, 6]); - /// } - /// - /// { - /// let (left, right) = v.split_at(6); - /// assert!(left == [1, 2, 3, 4, 5, 6]); - /// assert!(right == []); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { - core_slice::SliceExt::split_at(self, mid) - } - - /// Divides one mutable slice into two at an index. - /// - /// The first will contain all indices from `[0, mid)` (excluding - /// the index `mid` itself) and the second will contain all - /// indices from `[mid, len)` (excluding the index `len` itself). - /// - /// # Panics - /// - /// Panics if `mid > len`. - /// - /// # Examples - /// - /// ``` - /// let mut v = [1, 0, 3, 0, 5, 6]; - /// // scoped to restrict the lifetime of the borrows - /// { - /// let (left, right) = v.split_at_mut(2); - /// assert!(left == [1, 0]); - /// assert!(right == [3, 0, 5, 6]); - /// left[1] = 2; - /// right[1] = 4; - /// } - /// assert!(v == [1, 2, 3, 4, 5, 6]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - core_slice::SliceExt::split_at_mut(self, mid) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let slice = [10, 40, 33, 20]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10, 40]); - /// assert_eq!(iter.next().unwrap(), &[20]); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If the first element is matched, an empty slice will be the first item - /// returned by the iterator. Similarly, if the last element in the slice - /// is matched, an empty slice will be the last item returned by the - /// iterator: - /// - /// ``` - /// let slice = [10, 40, 33]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10, 40]); - /// assert_eq!(iter.next().unwrap(), &[]); - /// assert!(iter.next().is_none()); - /// ``` - /// - /// If two matched elements are directly adjacent, an empty slice will be - /// present between them: - /// - /// ``` - /// let slice = [10, 6, 33, 20]; - /// let mut iter = slice.split(|num| num % 3 == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[10]); - /// assert_eq!(iter.next().unwrap(), &[]); - /// assert_eq!(iter.next().unwrap(), &[20]); - /// assert!(iter.next().is_none()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split(&self, pred: F) -> Split - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::split(self, pred) - } - - /// Returns an iterator over mutable subslices separated by elements that - /// match `pred`. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let mut v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.split_mut(|num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(v, [1, 40, 30, 1, 60, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_mut(&mut self, pred: F) -> SplitMut - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::split_mut(self, pred) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, starting at the end of the slice and working backwards. - /// The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// - /// let slice = [11, 22, 33, 0, 44, 55]; - /// let mut iter = slice.rsplit(|num| *num == 0); - /// - /// assert_eq!(iter.next().unwrap(), &[44, 55]); - /// assert_eq!(iter.next().unwrap(), &[11, 22, 33]); - /// assert_eq!(iter.next(), None); - /// ``` - /// - /// As with `split()`, if the first or last element is matched, an empty - /// slice will be the first (or last) item returned by the iterator. - /// - /// ``` - /// let v = &[0, 1, 1, 2, 3, 5, 8]; - /// let mut it = v.rsplit(|n| *n % 2 == 0); - /// assert_eq!(it.next().unwrap(), &[]); - /// assert_eq!(it.next().unwrap(), &[3, 5]); - /// assert_eq!(it.next().unwrap(), &[1, 1]); - /// assert_eq!(it.next().unwrap(), &[]); - /// assert_eq!(it.next(), None); - /// ``` - #[stable(feature = "slice_rsplit", since = "1.27.0")] - #[inline] - pub fn rsplit(&self, pred: F) -> RSplit - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::rsplit(self, pred) - } - - /// Returns an iterator over mutable subslices separated by elements that - /// match `pred`, starting at the end of the slice and working - /// backwards. The matched element is not contained in the subslices. - /// - /// # Examples - /// - /// ``` - /// let mut v = [100, 400, 300, 200, 600, 500]; - /// - /// let mut count = 0; - /// for group in v.rsplit_mut(|num| *num % 3 == 0) { - /// count += 1; - /// group[0] = count; - /// } - /// assert_eq!(v, [3, 400, 300, 2, 600, 1]); - /// ``` - /// - #[stable(feature = "slice_rsplit", since = "1.27.0")] - #[inline] - pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::rsplit_mut(self, pred) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, limited to returning at most `n` items. The matched element is - /// not contained in the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// Print the slice split once by numbers divisible by 3 (i.e. `[10, 40]`, - /// `[20, 60, 50]`): - /// - /// ``` - /// let v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.splitn(2, |num| *num % 3 == 0) { - /// println!("{:?}", group); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn(&self, n: usize, pred: F) -> SplitN - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::splitn(self, n, pred) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred`, limited to returning at most `n` items. The matched element is - /// not contained in the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// ``` - /// let mut v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.splitn_mut(2, |num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(v, [1, 40, 30, 1, 60, 50]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::splitn_mut(self, n, pred) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred` limited to returning at most `n` items. This starts at the end of - /// the slice and works backwards. The matched element is not contained in - /// the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// Print the slice split once, starting from the end, by numbers divisible - /// by 3 (i.e. `[50]`, `[10, 40, 30, 20]`): - /// - /// ``` - /// let v = [10, 40, 30, 20, 60, 50]; - /// - /// for group in v.rsplitn(2, |num| *num % 3 == 0) { - /// println!("{:?}", group); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::rsplitn(self, n, pred) - } - - /// Returns an iterator over subslices separated by elements that match - /// `pred` limited to returning at most `n` items. This starts at the end of - /// the slice and works backwards. The matched element is not contained in - /// the subslices. - /// - /// The last element returned, if any, will contain the remainder of the - /// slice. - /// - /// # Examples - /// - /// ``` - /// let mut s = [10, 40, 30, 20, 60, 50]; - /// - /// for group in s.rsplitn_mut(2, |num| *num % 3 == 0) { - /// group[0] = 1; - /// } - /// assert_eq!(s, [1, 40, 30, 20, 60, 1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut - where F: FnMut(&T) -> bool - { - core_slice::SliceExt::rsplitn_mut(self, n, pred) - } - - /// Returns `true` if the slice contains an element with the given value. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.contains(&30)); - /// assert!(!v.contains(&50)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, x: &T) -> bool - where T: PartialEq - { - core_slice::SliceExt::contains(self, x) - } - - /// Returns `true` if `needle` is a prefix of the slice. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.starts_with(&[10])); - /// assert!(v.starts_with(&[10, 40])); - /// assert!(!v.starts_with(&[50])); - /// assert!(!v.starts_with(&[10, 50])); - /// ``` - /// - /// Always returns `true` if `needle` is an empty slice: - /// - /// ``` - /// let v = &[10, 40, 30]; - /// assert!(v.starts_with(&[])); - /// let v: &[u8] = &[]; - /// assert!(v.starts_with(&[])); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with(&self, needle: &[T]) -> bool - where T: PartialEq - { - core_slice::SliceExt::starts_with(self, needle) - } - - /// Returns `true` if `needle` is a suffix of the slice. - /// - /// # Examples - /// - /// ``` - /// let v = [10, 40, 30]; - /// assert!(v.ends_with(&[30])); - /// assert!(v.ends_with(&[40, 30])); - /// assert!(!v.ends_with(&[50])); - /// assert!(!v.ends_with(&[50, 30])); - /// ``` - /// - /// Always returns `true` if `needle` is an empty slice: - /// - /// ``` - /// let v = &[10, 40, 30]; - /// assert!(v.ends_with(&[])); - /// let v: &[u8] = &[]; - /// assert!(v.ends_with(&[])); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with(&self, needle: &[T]) -> bool - where T: PartialEq - { - core_slice::SliceExt::ends_with(self, needle) - } - - /// Binary searches this sorted slice for a given element. - /// - /// If the value is found then `Ok` is returned, containing the - /// index of the matching element; if the value is not found then - /// `Err` is returned, containing the index where a matching - /// element could be inserted while maintaining sorted order. - /// - /// # Examples - /// - /// Looks up a series of four elements. The first is found, with a - /// uniquely determined position; the second and third are not - /// found; the fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; - /// - /// assert_eq!(s.binary_search(&13), Ok(9)); - /// assert_eq!(s.binary_search(&4), Err(7)); - /// assert_eq!(s.binary_search(&100), Err(13)); - /// let r = s.binary_search(&1); - /// assert!(match r { Ok(1...4) => true, _ => false, }); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn binary_search(&self, x: &T) -> Result - where T: Ord - { - core_slice::SliceExt::binary_search(self, x) - } - - /// Binary searches this sorted slice with a comparator function. - /// - /// The comparator function should implement an order consistent - /// with the sort order of the underlying slice, returning an - /// order code that indicates whether its argument is `Less`, - /// `Equal` or `Greater` the desired target. - /// - /// If a matching value is found then returns `Ok`, containing - /// the index for the matched element; if no match is found then - /// `Err` is returned, containing the index where a matching - /// element could be inserted while maintaining sorted order. - /// - /// # Examples - /// - /// Looks up a series of four elements. The first is found, with a - /// uniquely determined position; the second and third are not - /// found; the fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; - /// - /// let seek = 13; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9)); - /// let seek = 4; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7)); - /// let seek = 100; - /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13)); - /// let seek = 1; - /// let r = s.binary_search_by(|probe| probe.cmp(&seek)); - /// assert!(match r { Ok(1...4) => true, _ => false, }); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result - where F: FnMut(&'a T) -> Ordering - { - core_slice::SliceExt::binary_search_by(self, f) - } - - /// Binary searches this sorted slice with a key extraction function. - /// - /// Assumes that the slice is sorted by the key, for instance with - /// [`sort_by_key`] using the same key extraction function. - /// - /// If a matching value is found then returns `Ok`, containing the - /// index for the matched element; if no match is found then `Err` - /// is returned, containing the index where a matching element could - /// be inserted while maintaining sorted order. - /// - /// [`sort_by_key`]: #method.sort_by_key - /// - /// # Examples - /// - /// Looks up a series of four elements in a slice of pairs sorted by - /// their second elements. The first is found, with a uniquely - /// determined position; the second and third are not found; the - /// fourth could match any position in `[1, 4]`. - /// - /// ``` - /// let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1), - /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), - /// (1, 21), (2, 34), (4, 55)]; - /// - /// assert_eq!(s.binary_search_by_key(&13, |&(a,b)| b), Ok(9)); - /// assert_eq!(s.binary_search_by_key(&4, |&(a,b)| b), Err(7)); - /// assert_eq!(s.binary_search_by_key(&100, |&(a,b)| b), Err(13)); - /// let r = s.binary_search_by_key(&1, |&(a,b)| b); - /// assert!(match r { Ok(1...4) => true, _ => false, }); - /// ``` - #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] - #[inline] - pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result - where F: FnMut(&'a T) -> B, - B: Ord - { - core_slice::SliceExt::binary_search_by_key(self, b, f) - } + #[cfg(stage0)] + slice_core_methods!(); /// Sorts the slice. /// @@ -1402,345 +355,6 @@ impl [T] { sort_by_key!(usize, self, f) } - /// Sorts the slice, but may not preserve the order of equal elements. - /// - /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), - /// and `O(n log n)` worst-case. - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// It is typically faster than stable sorting, except in a few special cases, e.g. when the - /// slice consists of several concatenated sorted sequences. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5, 4, 1, -3, 2]; - /// - /// v.sort_unstable(); - /// assert!(v == [-5, -3, 1, 2, 4]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable(&mut self) - where T: Ord - { - core_slice::SliceExt::sort_unstable(self); - } - - /// Sorts the slice with a comparator function, but may not preserve the order of equal - /// elements. - /// - /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), - /// and `O(n log n)` worst-case. - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// It is typically faster than stable sorting, except in a few special cases, e.g. when the - /// slice consists of several concatenated sorted sequences. - /// - /// # Examples - /// - /// ``` - /// let mut v = [5, 4, 1, 3, 2]; - /// v.sort_unstable_by(|a, b| a.cmp(b)); - /// assert!(v == [1, 2, 3, 4, 5]); - /// - /// // reverse sorting - /// v.sort_unstable_by(|a, b| b.cmp(a)); - /// assert!(v == [5, 4, 3, 2, 1]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable_by(&mut self, compare: F) - where F: FnMut(&T, &T) -> Ordering - { - core_slice::SliceExt::sort_unstable_by(self, compare); - } - - /// Sorts the slice with a key extraction function, but may not preserve the order of equal - /// elements. - /// - /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), - /// and `O(m n log(m n))` worst-case, where the key function is `O(m)`. - /// - /// # Current implementation - /// - /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, - /// which combines the fast average case of randomized quicksort with the fast worst case of - /// heapsort, while achieving linear time on slices with certain patterns. It uses some - /// randomization to avoid degenerate cases, but with a fixed seed to always provide - /// deterministic behavior. - /// - /// # Examples - /// - /// ``` - /// let mut v = [-5i32, 4, 1, -3, 2]; - /// - /// v.sort_unstable_by_key(|k| k.abs()); - /// assert!(v == [1, 2, -3, 4, -5]); - /// ``` - /// - /// [pdqsort]: https://github.com/orlp/pdqsort - #[stable(feature = "sort_unstable", since = "1.20.0")] - #[inline] - pub fn sort_unstable_by_key(&mut self, f: F) - where F: FnMut(&T) -> K, K: Ord - { - core_slice::SliceExt::sort_unstable_by_key(self, f); - } - - /// Rotates the slice in-place such that the first `mid` elements of the - /// slice move to the end while the last `self.len() - mid` elements move to - /// the front. After calling `rotate_left`, the element previously at index - /// `mid` will become the first element in the slice. - /// - /// # Panics - /// - /// This function will panic if `mid` is greater than the length of the - /// slice. Note that `mid == self.len()` does _not_ panic and is a no-op - /// rotation. - /// - /// # Complexity - /// - /// Takes linear (in `self.len()`) time. - /// - /// # Examples - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a.rotate_left(2); - /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']); - /// ``` - /// - /// Rotating a subslice: - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a[1..5].rotate_left(1); - /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); - /// ``` - #[stable(feature = "slice_rotate", since = "1.26.0")] - pub fn rotate_left(&mut self, mid: usize) { - core_slice::SliceExt::rotate_left(self, mid); - } - - /// Rotates the slice in-place such that the first `self.len() - k` - /// elements of the slice move to the end while the last `k` elements move - /// to the front. After calling `rotate_right`, the element previously at - /// index `self.len() - k` will become the first element in the slice. - /// - /// # Panics - /// - /// This function will panic if `k` is greater than the length of the - /// slice. Note that `k == self.len()` does _not_ panic and is a no-op - /// rotation. - /// - /// # Complexity - /// - /// Takes linear (in `self.len()`) time. - /// - /// # Examples - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a.rotate_right(2); - /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']); - /// ``` - /// - /// Rotate a subslice: - /// - /// ``` - /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; - /// a[1..5].rotate_right(1); - /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); - /// ``` - #[stable(feature = "slice_rotate", since = "1.26.0")] - pub fn rotate_right(&mut self, k: usize) { - core_slice::SliceExt::rotate_right(self, k); - } - - /// Copies the elements from `src` into `self`. - /// - /// The length of `src` must be the same as `self`. - /// - /// If `src` implements `Copy`, it can be more performant to use - /// [`copy_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// Cloning two elements from a slice into another: - /// - /// ``` - /// let src = [1, 2, 3, 4]; - /// let mut dst = [0, 0]; - /// - /// dst.clone_from_slice(&src[2..]); - /// - /// assert_eq!(src, [1, 2, 3, 4]); - /// assert_eq!(dst, [3, 4]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference with no - /// immutable references to a particular piece of data in a particular - /// scope. Because of this, attempting to use `clone_from_slice` on a - /// single slice will result in a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// slice[..2].clone_from_slice(&slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.clone_from_slice(&right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 4, 5]); - /// ``` - /// - /// [`copy_from_slice`]: #method.copy_from_slice - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "clone_from_slice", since = "1.7.0")] - pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - core_slice::SliceExt::clone_from_slice(self, src) - } - - /// Copies all elements from `src` into `self`, using a memcpy. - /// - /// The length of `src` must be the same as `self`. - /// - /// If `src` does not implement `Copy`, use [`clone_from_slice`]. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Examples - /// - /// Copying two elements from a slice into another: - /// - /// ``` - /// let src = [1, 2, 3, 4]; - /// let mut dst = [0, 0]; - /// - /// dst.copy_from_slice(&src[2..]); - /// - /// assert_eq!(src, [1, 2, 3, 4]); - /// assert_eq!(dst, [3, 4]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference with no - /// immutable references to a particular piece of data in a particular - /// scope. Because of this, attempting to use `copy_from_slice` on a - /// single slice will result in a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// slice[..2].copy_from_slice(&slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.copy_from_slice(&right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 4, 5]); - /// ``` - /// - /// [`clone_from_slice`]: #method.clone_from_slice - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "copy_from_slice", since = "1.9.0")] - pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - core_slice::SliceExt::copy_from_slice(self, src) - } - - /// Swaps all elements in `self` with those in `other`. - /// - /// The length of `other` must be the same as `self`. - /// - /// # Panics - /// - /// This function will panic if the two slices have different lengths. - /// - /// # Example - /// - /// Swapping two elements across slices: - /// - /// ``` - /// let mut slice1 = [0, 0]; - /// let mut slice2 = [1, 2, 3, 4]; - /// - /// slice1.swap_with_slice(&mut slice2[2..]); - /// - /// assert_eq!(slice1, [3, 4]); - /// assert_eq!(slice2, [1, 2, 0, 0]); - /// ``` - /// - /// Rust enforces that there can only be one mutable reference to a - /// particular piece of data in a particular scope. Because of this, - /// attempting to use `swap_with_slice` on a single slice will result in - /// a compile failure: - /// - /// ```compile_fail - /// let mut slice = [1, 2, 3, 4, 5]; - /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! - /// ``` - /// - /// To work around this, we can use [`split_at_mut`] to create two distinct - /// mutable sub-slices from a slice: - /// - /// ``` - /// let mut slice = [1, 2, 3, 4, 5]; - /// - /// { - /// let (left, right) = slice.split_at_mut(2); - /// left.swap_with_slice(&mut right[1..]); - /// } - /// - /// assert_eq!(slice, [4, 5, 3, 1, 2]); - /// ``` - /// - /// [`split_at_mut`]: #method.split_at_mut - #[stable(feature = "swap_with_slice", since = "1.27.0")] - pub fn swap_with_slice(&mut self, other: &mut [T]) { - core_slice::SliceExt::swap_with_slice(self, other) - } - /// Copies `self` into a new `Vec`. /// /// # Examples diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 88bd0444233..f4fafe304c0 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -229,7 +229,7 @@ macro_rules! test_v512 { ($item:item) => {}; } #[allow(unused_macros)] macro_rules! vector_impl { ($([$f:ident, $($args:tt)*]),*) => { $($f!($($args)*);)* } } #[path = "../stdsimd/coresimd/mod.rs"] -#[allow(missing_docs, missing_debug_implementations, dead_code)] +#[allow(missing_docs, missing_debug_implementations, dead_code, unused_imports)] #[unstable(feature = "stdsimd", issue = "48556")] #[cfg(not(stage0))] // allow changes to how stdsimd works in stage0 mod coresimd; diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index cc3ad71117a..32c1531bdc0 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -58,6 +58,7 @@ pub use result::Result::{self, Ok, Err}; // Re-exported extension traits for primitive types #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] +#[cfg(stage0)] pub use slice::SliceExt; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 4cda1c8778a..0a260c663c2 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -755,6 +755,1410 @@ impl SliceExt for [T] { } } +// FIXME: remove (inline) this macro and the SliceExt trait +// when updating to a bootstrap compiler that has the new lang items. +#[cfg_attr(stage0, macro_export)] +#[unstable(feature = "core_slice_ext", issue = "32110")] +macro_rules! slice_core_methods { () => { + /// Returns the number of elements in the slice. + /// + /// # Examples + /// + /// ``` + /// let a = [1, 2, 3]; + /// assert_eq!(a.len(), 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn len(&self) -> usize { + SliceExt::len(self) + } + + /// Returns `true` if the slice has a length of 0. + /// + /// # Examples + /// + /// ``` + /// let a = [1, 2, 3]; + /// assert!(!a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_empty(&self) -> bool { + SliceExt::is_empty(self) + } + + /// Returns the first element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&10), v.first()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(None, w.first()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn first(&self) -> Option<&T> { + SliceExt::first(self) + } + + /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(first) = x.first_mut() { + /// *first = 5; + /// } + /// assert_eq!(x, &[5, 1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn first_mut(&mut self) -> Option<&mut T> { + SliceExt::first_mut(self) + } + + /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &[0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first() { + /// assert_eq!(first, &0); + /// assert_eq!(elements, &[1, 2]); + /// } + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_first(&self) -> Option<(&T, &[T])> { + SliceExt::split_first(self) + } + + /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((first, elements)) = x.split_first_mut() { + /// *first = 3; + /// elements[0] = 4; + /// elements[1] = 5; + /// } + /// assert_eq!(x, &[3, 4, 5]); + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { + SliceExt::split_first_mut(self) + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &[0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last() { + /// assert_eq!(last, &2); + /// assert_eq!(elements, &[0, 1]); + /// } + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_last(&self) -> Option<(&T, &[T])> { + SliceExt::split_last(self) + + } + + /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some((last, elements)) = x.split_last_mut() { + /// *last = 3; + /// elements[0] = 4; + /// elements[1] = 5; + /// } + /// assert_eq!(x, &[4, 5, 3]); + /// ``` + #[stable(feature = "slice_splits", since = "1.5.0")] + #[inline] + pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { + SliceExt::split_last_mut(self) + } + + /// Returns the last element of the slice, or `None` if it is empty. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&30), v.last()); + /// + /// let w: &[i32] = &[]; + /// assert_eq!(None, w.last()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn last(&self) -> Option<&T> { + SliceExt::last(self) + } + + /// Returns a mutable pointer to the last item in the slice. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(last) = x.last_mut() { + /// *last = 10; + /// } + /// assert_eq!(x, &[0, 1, 10]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn last_mut(&mut self) -> Option<&mut T> { + SliceExt::last_mut(self) + } + + /// Returns a reference to an element or subslice depending on the type of + /// index. + /// + /// - If given a position, returns a reference to the element at that + /// position or `None` if out of bounds. + /// - If given a range, returns the subslice corresponding to that range, + /// or `None` if out of bounds. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert_eq!(Some(&40), v.get(1)); + /// assert_eq!(Some(&[10, 40][..]), v.get(0..2)); + /// assert_eq!(None, v.get(3)); + /// assert_eq!(None, v.get(0..4)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get(&self, index: I) -> Option<&I::Output> + where I: SliceIndex + { + SliceExt::get(self, index) + } + + /// Returns a mutable reference to an element or subslice depending on the + /// type of index (see [`get`]) or `None` if the index is out of bounds. + /// + /// [`get`]: #method.get + /// + /// # Examples + /// + /// ``` + /// let x = &mut [0, 1, 2]; + /// + /// if let Some(elem) = x.get_mut(1) { + /// *elem = 42; + /// } + /// assert_eq!(x, &[0, 42, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> + where I: SliceIndex + { + SliceExt::get_mut(self, index) + } + + /// Returns a reference to an element or subslice, without doing bounds + /// checking. + /// + /// This is generally not recommended, use with caution! For a safe + /// alternative see [`get`]. + /// + /// [`get`]: #method.get + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// + /// unsafe { + /// assert_eq!(x.get_unchecked(1), &2); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub unsafe fn get_unchecked(&self, index: I) -> &I::Output + where I: SliceIndex + { + SliceExt::get_unchecked(self, index) + } + + /// Returns a mutable reference to an element or subslice, without doing + /// bounds checking. + /// + /// This is generally not recommended, use with caution! For a safe + /// alternative see [`get_mut`]. + /// + /// [`get_mut`]: #method.get_mut + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// + /// unsafe { + /// let elem = x.get_unchecked_mut(1); + /// *elem = 13; + /// } + /// assert_eq!(x, &[1, 13, 4]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output + where I: SliceIndex + { + SliceExt::get_unchecked_mut(self, index) + } + + /// Returns a raw pointer to the slice's buffer. + /// + /// The caller must ensure that the slice outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// let x_ptr = x.as_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// assert_eq!(x.get_unchecked(i), &*x_ptr.offset(i as isize)); + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn as_ptr(&self) -> *const T { + SliceExt::as_ptr(self) + } + + /// Returns an unsafe mutable pointer to the slice's buffer. + /// + /// The caller must ensure that the slice outlives the pointer this + /// function returns, or else it will end up pointing to garbage. + /// + /// Modifying the container referenced by this slice may cause its buffer + /// to be reallocated, which would also make any pointers to it invalid. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// let x_ptr = x.as_mut_ptr(); + /// + /// unsafe { + /// for i in 0..x.len() { + /// *x_ptr.offset(i as isize) += 2; + /// } + /// } + /// assert_eq!(x, &[3, 4, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn as_mut_ptr(&mut self) -> *mut T { + SliceExt::as_mut_ptr(self) + } + + /// Swaps two elements in the slice. + /// + /// # Arguments + /// + /// * a - The index of the first element + /// * b - The index of the second element + /// + /// # Panics + /// + /// Panics if `a` or `b` are out of bounds. + /// + /// # Examples + /// + /// ``` + /// let mut v = ["a", "b", "c", "d"]; + /// v.swap(1, 3); + /// assert!(v == ["a", "d", "c", "b"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn swap(&mut self, a: usize, b: usize) { + SliceExt::swap(self, a, b) + } + + /// Reverses the order of elements in the slice, in place. + /// + /// # Examples + /// + /// ``` + /// let mut v = [1, 2, 3]; + /// v.reverse(); + /// assert!(v == [3, 2, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn reverse(&mut self) { + SliceExt::reverse(self) + } + + /// Returns an iterator over the slice. + /// + /// # Examples + /// + /// ``` + /// let x = &[1, 2, 4]; + /// let mut iterator = x.iter(); + /// + /// assert_eq!(iterator.next(), Some(&1)); + /// assert_eq!(iterator.next(), Some(&2)); + /// assert_eq!(iterator.next(), Some(&4)); + /// assert_eq!(iterator.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn iter(&self) -> Iter { + SliceExt::iter(self) + } + + /// Returns an iterator that allows modifying each value. + /// + /// # Examples + /// + /// ``` + /// let x = &mut [1, 2, 4]; + /// for elem in x.iter_mut() { + /// *elem += 2; + /// } + /// assert_eq!(x, &[3, 4, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn iter_mut(&mut self) -> IterMut { + SliceExt::iter_mut(self) + } + + /// Returns an iterator over all contiguous windows of length + /// `size`. The windows overlap. If the slice is shorter than + /// `size`, the iterator returns no values. + /// + /// # Panics + /// + /// Panics if `size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['r', 'u', 's', 't']; + /// let mut iter = slice.windows(2); + /// assert_eq!(iter.next().unwrap(), &['r', 'u']); + /// assert_eq!(iter.next().unwrap(), &['u', 's']); + /// assert_eq!(iter.next().unwrap(), &['s', 't']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If the slice is shorter than `size`: + /// + /// ``` + /// let slice = ['f', 'o', 'o']; + /// let mut iter = slice.windows(4); + /// assert!(iter.next().is_none()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn windows(&self, size: usize) -> Windows { + SliceExt::windows(self, size) + } + + /// Returns an iterator over `chunk_size` elements of the slice at a + /// time. The chunks are slices and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last chunk will + /// not have length `chunk_size`. + /// + /// See [`exact_chunks`] for a variant of this iterator that returns chunks + /// of always exactly `chunk_size` elements. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.chunks(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert_eq!(iter.next().unwrap(), &['m']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// [`exact_chunks`]: #method.exact_chunks + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chunks(&self, chunk_size: usize) -> Chunks { + SliceExt::chunks(self, chunk_size) + } + + /// Returns an iterator over `chunk_size` elements of the slice at a + /// time. The chunks are slices and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last up to `chunk_size-1` + /// elements will be omitted. + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler + /// can often optimize the resulting code better than in the case of + /// [`chunks`]. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_chunks)] + /// + /// let slice = ['l', 'o', 'r', 'e', 'm']; + /// let mut iter = slice.exact_chunks(2); + /// assert_eq!(iter.next().unwrap(), &['l', 'o']); + /// assert_eq!(iter.next().unwrap(), &['r', 'e']); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// [`chunks`]: #method.chunks + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { + SliceExt::exact_chunks(self, chunk_size) + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time. + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last chunk will not + /// have length `chunk_size`. + /// + /// See [`exact_chunks_mut`] for a variant of this iterator that returns chunks + /// of always exactly `chunk_size` elements. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.chunks_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 3]); + /// ``` + /// + /// [`exact_chunks_mut`]: #method.exact_chunks_mut + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { + SliceExt::chunks_mut(self, chunk_size) + } + + /// Returns an iterator over `chunk_size` elements of the slice at a time. + /// The chunks are mutable slices, and do not overlap. If `chunk_size` does + /// not divide the length of the slice, then the last up to `chunk_size-1` + /// elements will be omitted. + /// + /// + /// Due to each chunk having exactly `chunk_size` elements, the compiler + /// can often optimize the resulting code better than in the case of + /// [`chunks_mut`]. + /// + /// # Panics + /// + /// Panics if `chunk_size` is 0. + /// + /// # Examples + /// + /// ``` + /// #![feature(exact_chunks)] + /// + /// let v = &mut [0, 0, 0, 0, 0]; + /// let mut count = 1; + /// + /// for chunk in v.exact_chunks_mut(2) { + /// for elem in chunk.iter_mut() { + /// *elem += count; + /// } + /// count += 1; + /// } + /// assert_eq!(v, &[1, 1, 2, 2, 0]); + /// ``` + /// + /// [`chunks_mut`]: #method.chunks_mut + #[unstable(feature = "exact_chunks", issue = "47115")] + #[inline] + pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { + SliceExt::exact_chunks_mut(self, chunk_size) + } + + /// Divides one slice into two at an index. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// let v = [1, 2, 3, 4, 5, 6]; + /// + /// { + /// let (left, right) = v.split_at(0); + /// assert!(left == []); + /// assert!(right == [1, 2, 3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at(2); + /// assert!(left == [1, 2]); + /// assert!(right == [3, 4, 5, 6]); + /// } + /// + /// { + /// let (left, right) = v.split_at(6); + /// assert!(left == [1, 2, 3, 4, 5, 6]); + /// assert!(right == []); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { + SliceExt::split_at(self, mid) + } + + /// Divides one mutable slice into two at an index. + /// + /// The first will contain all indices from `[0, mid)` (excluding + /// the index `mid` itself) and the second will contain all + /// indices from `[mid, len)` (excluding the index `len` itself). + /// + /// # Panics + /// + /// Panics if `mid > len`. + /// + /// # Examples + /// + /// ``` + /// let mut v = [1, 0, 3, 0, 5, 6]; + /// // scoped to restrict the lifetime of the borrows + /// { + /// let (left, right) = v.split_at_mut(2); + /// assert!(left == [1, 0]); + /// assert!(right == [3, 0, 5, 6]); + /// left[1] = 2; + /// right[1] = 4; + /// } + /// assert!(v == [1, 2, 3, 4, 5, 6]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { + SliceExt::split_at_mut(self, mid) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let slice = [10, 40, 33, 20]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10, 40]); + /// assert_eq!(iter.next().unwrap(), &[20]); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If the first element is matched, an empty slice will be the first item + /// returned by the iterator. Similarly, if the last element in the slice + /// is matched, an empty slice will be the last item returned by the + /// iterator: + /// + /// ``` + /// let slice = [10, 40, 33]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10, 40]); + /// assert_eq!(iter.next().unwrap(), &[]); + /// assert!(iter.next().is_none()); + /// ``` + /// + /// If two matched elements are directly adjacent, an empty slice will be + /// present between them: + /// + /// ``` + /// let slice = [10, 6, 33, 20]; + /// let mut iter = slice.split(|num| num % 3 == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[10]); + /// assert_eq!(iter.next().unwrap(), &[]); + /// assert_eq!(iter.next().unwrap(), &[20]); + /// assert!(iter.next().is_none()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split(&self, pred: F) -> Split + where F: FnMut(&T) -> bool + { + SliceExt::split(self, pred) + } + + /// Returns an iterator over mutable subslices separated by elements that + /// match `pred`. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let mut v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.split_mut(|num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(v, [1, 40, 30, 1, 60, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_mut(&mut self, pred: F) -> SplitMut + where F: FnMut(&T) -> bool + { + SliceExt::split_mut(self, pred) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, starting at the end of the slice and working backwards. + /// The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let slice = [11, 22, 33, 0, 44, 55]; + /// let mut iter = slice.rsplit(|num| *num == 0); + /// + /// assert_eq!(iter.next().unwrap(), &[44, 55]); + /// assert_eq!(iter.next().unwrap(), &[11, 22, 33]); + /// assert_eq!(iter.next(), None); + /// ``` + /// + /// As with `split()`, if the first or last element is matched, an empty + /// slice will be the first (or last) item returned by the iterator. + /// + /// ``` + /// let v = &[0, 1, 1, 2, 3, 5, 8]; + /// let mut it = v.rsplit(|n| *n % 2 == 0); + /// assert_eq!(it.next().unwrap(), &[]); + /// assert_eq!(it.next().unwrap(), &[3, 5]); + /// assert_eq!(it.next().unwrap(), &[1, 1]); + /// assert_eq!(it.next().unwrap(), &[]); + /// assert_eq!(it.next(), None); + /// ``` + #[stable(feature = "slice_rsplit", since = "1.27.0")] + #[inline] + pub fn rsplit(&self, pred: F) -> RSplit + where F: FnMut(&T) -> bool + { + SliceExt::rsplit(self, pred) + } + + /// Returns an iterator over mutable subslices separated by elements that + /// match `pred`, starting at the end of the slice and working + /// backwards. The matched element is not contained in the subslices. + /// + /// # Examples + /// + /// ``` + /// let mut v = [100, 400, 300, 200, 600, 500]; + /// + /// let mut count = 0; + /// for group in v.rsplit_mut(|num| *num % 3 == 0) { + /// count += 1; + /// group[0] = count; + /// } + /// assert_eq!(v, [3, 400, 300, 2, 600, 1]); + /// ``` + /// + #[stable(feature = "slice_rsplit", since = "1.27.0")] + #[inline] + pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut + where F: FnMut(&T) -> bool + { + SliceExt::rsplit_mut(self, pred) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, limited to returning at most `n` items. The matched element is + /// not contained in the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// Print the slice split once by numbers divisible by 3 (i.e. `[10, 40]`, + /// `[20, 60, 50]`): + /// + /// ``` + /// let v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.splitn(2, |num| *num % 3 == 0) { + /// println!("{:?}", group); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn(&self, n: usize, pred: F) -> SplitN + where F: FnMut(&T) -> bool + { + SliceExt::splitn(self, n, pred) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred`, limited to returning at most `n` items. The matched element is + /// not contained in the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// ``` + /// let mut v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.splitn_mut(2, |num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(v, [1, 40, 30, 1, 60, 50]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut + where F: FnMut(&T) -> bool + { + SliceExt::splitn_mut(self, n, pred) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred` limited to returning at most `n` items. This starts at the end of + /// the slice and works backwards. The matched element is not contained in + /// the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// Print the slice split once, starting from the end, by numbers divisible + /// by 3 (i.e. `[50]`, `[10, 40, 30, 20]`): + /// + /// ``` + /// let v = [10, 40, 30, 20, 60, 50]; + /// + /// for group in v.rsplitn(2, |num| *num % 3 == 0) { + /// println!("{:?}", group); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN + where F: FnMut(&T) -> bool + { + SliceExt::rsplitn(self, n, pred) + } + + /// Returns an iterator over subslices separated by elements that match + /// `pred` limited to returning at most `n` items. This starts at the end of + /// the slice and works backwards. The matched element is not contained in + /// the subslices. + /// + /// The last element returned, if any, will contain the remainder of the + /// slice. + /// + /// # Examples + /// + /// ``` + /// let mut s = [10, 40, 30, 20, 60, 50]; + /// + /// for group in s.rsplitn_mut(2, |num| *num % 3 == 0) { + /// group[0] = 1; + /// } + /// assert_eq!(s, [1, 40, 30, 20, 60, 1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut + where F: FnMut(&T) -> bool + { + SliceExt::rsplitn_mut(self, n, pred) + } + + /// Returns `true` if the slice contains an element with the given value. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.contains(&30)); + /// assert!(!v.contains(&50)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains(&self, x: &T) -> bool + where T: PartialEq + { + SliceExt::contains(self, x) + } + + /// Returns `true` if `needle` is a prefix of the slice. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.starts_with(&[10])); + /// assert!(v.starts_with(&[10, 40])); + /// assert!(!v.starts_with(&[50])); + /// assert!(!v.starts_with(&[10, 50])); + /// ``` + /// + /// Always returns `true` if `needle` is an empty slice: + /// + /// ``` + /// let v = &[10, 40, 30]; + /// assert!(v.starts_with(&[])); + /// let v: &[u8] = &[]; + /// assert!(v.starts_with(&[])); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn starts_with(&self, needle: &[T]) -> bool + where T: PartialEq + { + SliceExt::starts_with(self, needle) + } + + /// Returns `true` if `needle` is a suffix of the slice. + /// + /// # Examples + /// + /// ``` + /// let v = [10, 40, 30]; + /// assert!(v.ends_with(&[30])); + /// assert!(v.ends_with(&[40, 30])); + /// assert!(!v.ends_with(&[50])); + /// assert!(!v.ends_with(&[50, 30])); + /// ``` + /// + /// Always returns `true` if `needle` is an empty slice: + /// + /// ``` + /// let v = &[10, 40, 30]; + /// assert!(v.ends_with(&[])); + /// let v: &[u8] = &[]; + /// assert!(v.ends_with(&[])); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ends_with(&self, needle: &[T]) -> bool + where T: PartialEq + { + SliceExt::ends_with(self, needle) + } + + /// Binary searches this sorted slice for a given element. + /// + /// If the value is found then `Ok` is returned, containing the + /// index of the matching element; if the value is not found then + /// `Err` is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; + /// + /// assert_eq!(s.binary_search(&13), Ok(9)); + /// assert_eq!(s.binary_search(&4), Err(7)); + /// assert_eq!(s.binary_search(&100), Err(13)); + /// let r = s.binary_search(&1); + /// assert!(match r { Ok(1...4) => true, _ => false, }); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn binary_search(&self, x: &T) -> Result + where T: Ord + { + SliceExt::binary_search(self, x) + } + + /// Binary searches this sorted slice with a comparator function. + /// + /// The comparator function should implement an order consistent + /// with the sort order of the underlying slice, returning an + /// order code that indicates whether its argument is `Less`, + /// `Equal` or `Greater` the desired target. + /// + /// If a matching value is found then returns `Ok`, containing + /// the index for the matched element; if no match is found then + /// `Err` is returned, containing the index where a matching + /// element could be inserted while maintaining sorted order. + /// + /// # Examples + /// + /// Looks up a series of four elements. The first is found, with a + /// uniquely determined position; the second and third are not + /// found; the fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [0, 1, 1, 1, 1, 2, 3, 5, 8, 13, 21, 34, 55]; + /// + /// let seek = 13; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Ok(9)); + /// let seek = 4; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(7)); + /// let seek = 100; + /// assert_eq!(s.binary_search_by(|probe| probe.cmp(&seek)), Err(13)); + /// let seek = 1; + /// let r = s.binary_search_by(|probe| probe.cmp(&seek)); + /// assert!(match r { Ok(1...4) => true, _ => false, }); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result + where F: FnMut(&'a T) -> Ordering + { + SliceExt::binary_search_by(self, f) + } + + /// Binary searches this sorted slice with a key extraction function. + /// + /// Assumes that the slice is sorted by the key, for instance with + /// [`sort_by_key`] using the same key extraction function. + /// + /// If a matching value is found then returns `Ok`, containing the + /// index for the matched element; if no match is found then `Err` + /// is returned, containing the index where a matching element could + /// be inserted while maintaining sorted order. + /// + /// [`sort_by_key`]: #method.sort_by_key + /// + /// # Examples + /// + /// Looks up a series of four elements in a slice of pairs sorted by + /// their second elements. The first is found, with a uniquely + /// determined position; the second and third are not found; the + /// fourth could match any position in `[1, 4]`. + /// + /// ``` + /// let s = [(0, 0), (2, 1), (4, 1), (5, 1), (3, 1), + /// (1, 2), (2, 3), (4, 5), (5, 8), (3, 13), + /// (1, 21), (2, 34), (4, 55)]; + /// + /// assert_eq!(s.binary_search_by_key(&13, |&(a,b)| b), Ok(9)); + /// assert_eq!(s.binary_search_by_key(&4, |&(a,b)| b), Err(7)); + /// assert_eq!(s.binary_search_by_key(&100, |&(a,b)| b), Err(13)); + /// let r = s.binary_search_by_key(&1, |&(a,b)| b); + /// assert!(match r { Ok(1...4) => true, _ => false, }); + /// ``` + #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] + #[inline] + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result + where F: FnMut(&'a T) -> B, + B: Ord + { + SliceExt::binary_search_by_key(self, b, f) + } + + /// Sorts the slice, but may not preserve the order of equal elements. + /// + /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), + /// and `O(n log n)` worst-case. + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g. when the + /// slice consists of several concatenated sorted sequences. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5, 4, 1, -3, 2]; + /// + /// v.sort_unstable(); + /// assert!(v == [-5, -3, 1, 2, 4]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable(&mut self) + where T: Ord + { + SliceExt::sort_unstable(self); + } + + /// Sorts the slice with a comparator function, but may not preserve the order of equal + /// elements. + /// + /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), + /// and `O(n log n)` worst-case. + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// It is typically faster than stable sorting, except in a few special cases, e.g. when the + /// slice consists of several concatenated sorted sequences. + /// + /// # Examples + /// + /// ``` + /// let mut v = [5, 4, 1, 3, 2]; + /// v.sort_unstable_by(|a, b| a.cmp(b)); + /// assert!(v == [1, 2, 3, 4, 5]); + /// + /// // reverse sorting + /// v.sort_unstable_by(|a, b| b.cmp(a)); + /// assert!(v == [5, 4, 3, 2, 1]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable_by(&mut self, compare: F) + where F: FnMut(&T, &T) -> Ordering + { + SliceExt::sort_unstable_by(self, compare); + } + + /// Sorts the slice with a key extraction function, but may not preserve the order of equal + /// elements. + /// + /// This sort is unstable (i.e. may reorder equal elements), in-place (i.e. does not allocate), + /// and `O(m n log(m n))` worst-case, where the key function is `O(m)`. + /// + /// # Current implementation + /// + /// The current algorithm is based on [pattern-defeating quicksort][pdqsort] by Orson Peters, + /// which combines the fast average case of randomized quicksort with the fast worst case of + /// heapsort, while achieving linear time on slices with certain patterns. It uses some + /// randomization to avoid degenerate cases, but with a fixed seed to always provide + /// deterministic behavior. + /// + /// # Examples + /// + /// ``` + /// let mut v = [-5i32, 4, 1, -3, 2]; + /// + /// v.sort_unstable_by_key(|k| k.abs()); + /// assert!(v == [1, 2, -3, 4, -5]); + /// ``` + /// + /// [pdqsort]: https://github.com/orlp/pdqsort + #[stable(feature = "sort_unstable", since = "1.20.0")] + #[inline] + pub fn sort_unstable_by_key(&mut self, f: F) + where F: FnMut(&T) -> K, K: Ord + { + SliceExt::sort_unstable_by_key(self, f); + } + + /// Rotates the slice in-place such that the first `mid` elements of the + /// slice move to the end while the last `self.len() - mid` elements move to + /// the front. After calling `rotate_left`, the element previously at index + /// `mid` will become the first element in the slice. + /// + /// # Panics + /// + /// This function will panic if `mid` is greater than the length of the + /// slice. Note that `mid == self.len()` does _not_ panic and is a no-op + /// rotation. + /// + /// # Complexity + /// + /// Takes linear (in `self.len()`) time. + /// + /// # Examples + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a.rotate_left(2); + /// assert_eq!(a, ['c', 'd', 'e', 'f', 'a', 'b']); + /// ``` + /// + /// Rotating a subslice: + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a[1..5].rotate_left(1); + /// assert_eq!(a, ['a', 'c', 'd', 'e', 'b', 'f']); + /// ``` + #[stable(feature = "slice_rotate", since = "1.26.0")] + pub fn rotate_left(&mut self, mid: usize) { + SliceExt::rotate_left(self, mid); + } + + /// Rotates the slice in-place such that the first `self.len() - k` + /// elements of the slice move to the end while the last `k` elements move + /// to the front. After calling `rotate_right`, the element previously at + /// index `self.len() - k` will become the first element in the slice. + /// + /// # Panics + /// + /// This function will panic if `k` is greater than the length of the + /// slice. Note that `k == self.len()` does _not_ panic and is a no-op + /// rotation. + /// + /// # Complexity + /// + /// Takes linear (in `self.len()`) time. + /// + /// # Examples + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a.rotate_right(2); + /// assert_eq!(a, ['e', 'f', 'a', 'b', 'c', 'd']); + /// ``` + /// + /// Rotate a subslice: + /// + /// ``` + /// let mut a = ['a', 'b', 'c', 'd', 'e', 'f']; + /// a[1..5].rotate_right(1); + /// assert_eq!(a, ['a', 'e', 'b', 'c', 'd', 'f']); + /// ``` + #[stable(feature = "slice_rotate", since = "1.26.0")] + pub fn rotate_right(&mut self, k: usize) { + SliceExt::rotate_right(self, k); + } + + /// Copies the elements from `src` into `self`. + /// + /// The length of `src` must be the same as `self`. + /// + /// If `src` implements `Copy`, it can be more performant to use + /// [`copy_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// Cloning two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// dst.clone_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `clone_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].clone_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.clone_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); + /// ``` + /// + /// [`copy_from_slice`]: #method.copy_from_slice + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "clone_from_slice", since = "1.7.0")] + pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { + SliceExt::clone_from_slice(self, src) + } + + /// Copies all elements from `src` into `self`, using a memcpy. + /// + /// The length of `src` must be the same as `self`. + /// + /// If `src` does not implement `Copy`, use [`clone_from_slice`]. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Examples + /// + /// Copying two elements from a slice into another: + /// + /// ``` + /// let src = [1, 2, 3, 4]; + /// let mut dst = [0, 0]; + /// + /// dst.copy_from_slice(&src[2..]); + /// + /// assert_eq!(src, [1, 2, 3, 4]); + /// assert_eq!(dst, [3, 4]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference with no + /// immutable references to a particular piece of data in a particular + /// scope. Because of this, attempting to use `copy_from_slice` on a + /// single slice will result in a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// slice[..2].copy_from_slice(&slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.copy_from_slice(&right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 4, 5]); + /// ``` + /// + /// [`clone_from_slice`]: #method.clone_from_slice + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "copy_from_slice", since = "1.9.0")] + pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { + SliceExt::copy_from_slice(self, src) + } + + /// Swaps all elements in `self` with those in `other`. + /// + /// The length of `other` must be the same as `self`. + /// + /// # Panics + /// + /// This function will panic if the two slices have different lengths. + /// + /// # Example + /// + /// Swapping two elements across slices: + /// + /// ``` + /// let mut slice1 = [0, 0]; + /// let mut slice2 = [1, 2, 3, 4]; + /// + /// slice1.swap_with_slice(&mut slice2[2..]); + /// + /// assert_eq!(slice1, [3, 4]); + /// assert_eq!(slice2, [1, 2, 0, 0]); + /// ``` + /// + /// Rust enforces that there can only be one mutable reference to a + /// particular piece of data in a particular scope. Because of this, + /// attempting to use `swap_with_slice` on a single slice will result in + /// a compile failure: + /// + /// ```compile_fail + /// let mut slice = [1, 2, 3, 4, 5]; + /// slice[..2].swap_with_slice(&mut slice[3..]); // compile fail! + /// ``` + /// + /// To work around this, we can use [`split_at_mut`] to create two distinct + /// mutable sub-slices from a slice: + /// + /// ``` + /// let mut slice = [1, 2, 3, 4, 5]; + /// + /// { + /// let (left, right) = slice.split_at_mut(2); + /// left.swap_with_slice(&mut right[1..]); + /// } + /// + /// assert_eq!(slice, [4, 5, 3, 1, 2]); + /// ``` + /// + /// [`split_at_mut`]: #method.split_at_mut + #[stable(feature = "swap_with_slice", since = "1.27.0")] + pub fn swap_with_slice(&mut self, other: &mut [T]) { + SliceExt::swap_with_slice(self, other) + } +}} + +#[lang = "slice"] +#[cfg(not(test))] +#[cfg(not(stage0))] +impl [T] { + slice_core_methods!(); +} + +// FIXME: remove (inline) this macro +// when updating to a bootstrap compiler that has the new lang items. #[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_slice_ext", issue = "32110")] macro_rules! slice_u8_core_methods { () => { diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index f8cff8b186c..237a22925b4 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -214,6 +214,7 @@ language_item_table! { StrImplItem, "str", str_impl; SliceImplItem, "slice", slice_impl; SliceU8ImplItem, "slice_u8", slice_u8_impl; + SliceAllocImplItem, "slice_alloc", slice_alloc_impl; SliceU8AllocImplItem, "slice_u8_alloc", slice_u8_alloc_impl; ConstPtrImplItem, "const_ptr", const_ptr_impl; MutPtrImplItem, "mut_ptr", mut_ptr_impl; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index 7ba60f62791..ea4c2c08817 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -479,6 +479,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { let lang_def_id = lang_items.slice_u8_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + let lang_def_id = lang_items.slice_alloc_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); + let lang_def_id = lang_items.slice_u8_alloc_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); } diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index d7657bae8c5..91f9e3e6fba 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -138,7 +138,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TySlice(_) => { self.check_primitive_impl(def_id, lang_items.slice_impl(), - None, + lang_items.slice_alloc_impl(), "slice", "[T]", item.span); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 4acd12e5862..38af4350815 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -290,6 +290,7 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec Date: Sat, 7 Apr 2018 21:56:02 +0200 Subject: Replace StrExt with inherent str methods in libcore --- src/liballoc/lib.rs | 1 + src/liballoc/str.rs | 1835 +------------------- src/libcore/lib.rs | 1 + src/libcore/prelude/v1.rs | 1 + src/libcore/str/mod.rs | 1762 ++++++++++++++++++- src/librustc/middle/lang_items.rs | 1 + src/librustc_typeck/check/method/probe.rs | 3 + src/librustc_typeck/coherence/inherent_impls.rs | 2 +- src/librustdoc/clean/inline.rs | 1 + .../compile-fail/single-primitive-inherent-impl.rs | 2 +- src/test/rustdoc/issue-23511.rs | 2 +- 11 files changed, 1818 insertions(+), 1793 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 52011303543..702d7b70cd3 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -91,6 +91,7 @@ #![feature(const_fn)] #![feature(core_intrinsics)] #![cfg_attr(stage0, feature(core_slice_ext))] +#![cfg_attr(stage0, feature(core_str_ext))] #![feature(custom_attribute)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 686a0408a7c..82ba2f45711 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -40,6 +40,7 @@ use core::fmt; use core::str as core_str; +#[cfg(stage0)] use core::str::StrExt; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; @@ -76,7 +77,8 @@ pub use core::str::{from_utf8_unchecked, from_utf8_unchecked_mut, ParseBoolError pub use core::str::SplitWhitespace; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::pattern; - +#[stable(feature = "encode_utf16", since = "1.8.0")] +pub use core::str::EncodeUtf16; #[unstable(feature = "slice_concat_ext", reason = "trait should not have to exist", @@ -90,1729 +92,78 @@ impl> SliceConcatExt for [S] { } // `len` calculation may overflow but push_str will check boundaries - let len = self.iter().map(|s| s.borrow().len()).sum(); - let mut result = String::with_capacity(len); - - for s in self { - result.push_str(s.borrow()) - } - - result - } - - fn join(&self, sep: &str) -> String { - if self.is_empty() { - return String::new(); - } - - // concat is faster - if sep.is_empty() { - return self.concat(); - } - - // this is wrong without the guarantee that `self` is non-empty - // `len` calculation may overflow but push_str but will check boundaries - let len = sep.len() * (self.len() - 1) + - self.iter().map(|s| s.borrow().len()).sum::(); - let mut result = String::with_capacity(len); - let mut first = true; - - for s in self { - if first { - first = false; - } else { - result.push_str(sep); - } - result.push_str(s.borrow()); - } - result - } - - fn connect(&self, sep: &str) -> String { - self.join(sep) - } -} - -/// An iterator of [`u16`] over the string encoded as UTF-16. -/// -/// [`u16`]: ../../std/primitive.u16.html -/// -/// This struct is created by the [`encode_utf16`] method on [`str`]. -/// See its documentation for more. -/// -/// [`encode_utf16`]: ../../std/primitive.str.html#method.encode_utf16 -/// [`str`]: ../../std/primitive.str.html -#[derive(Clone)] -#[stable(feature = "encode_utf16", since = "1.8.0")] -pub struct EncodeUtf16<'a> { - chars: Chars<'a>, - extra: u16, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a> fmt::Debug for EncodeUtf16<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.pad("EncodeUtf16 { .. }") - } -} - -#[stable(feature = "encode_utf16", since = "1.8.0")] -impl<'a> Iterator for EncodeUtf16<'a> { - type Item = u16; - - #[inline] - fn next(&mut self) -> Option { - if self.extra != 0 { - let tmp = self.extra; - self.extra = 0; - return Some(tmp); - } - - let mut buf = [0; 2]; - self.chars.next().map(|ch| { - let n = ch.encode_utf16(&mut buf).len(); - if n == 2 { - self.extra = buf[1]; - } - buf[0] - }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let (low, high) = self.chars.size_hint(); - // every char gets either one u16 or two u16, - // so this iterator is between 1 or 2 times as - // long as the underlying iterator. - (low, high.and_then(|n| n.checked_mul(2))) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a> FusedIterator for EncodeUtf16<'a> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Borrow for String { - #[inline] - fn borrow(&self) -> &str { - &self[..] - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ToOwned for str { - type Owned = String; - fn to_owned(&self) -> String { - unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } - } - - fn clone_into(&self, target: &mut String) { - let mut b = mem::replace(target, String::new()).into_bytes(); - self.as_bytes().clone_into(&mut b); - *target = unsafe { String::from_utf8_unchecked(b) } - } -} - -/// Methods for string slices. -#[lang = "str"] -#[cfg(not(test))] -impl str { - /// Returns the length of `self`. - /// - /// This length is in bytes, not [`char`]s or graphemes. In other words, - /// it may not be what a human considers the length of the string. - /// - /// [`char`]: primitive.char.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let len = "foo".len(); - /// assert_eq!(3, len); - /// - /// let len = "ƒoo".len(); // fancy f! - /// assert_eq!(4, len); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn len(&self) -> usize { - core_str::StrExt::len(self) - } - - /// Returns `true` if `self` has a length of zero bytes. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = ""; - /// assert!(s.is_empty()); - /// - /// let s = "not empty"; - /// assert!(!s.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - core_str::StrExt::is_empty(self) - } - - /// Checks that `index`-th byte lies at the start and/or end of a - /// UTF-8 code point sequence. - /// - /// The start and end of the string (when `index == self.len()`) are - /// considered to be - /// boundaries. - /// - /// Returns `false` if `index` is greater than `self.len()`. - /// - /// # Examples - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// assert!(s.is_char_boundary(0)); - /// // start of `老` - /// assert!(s.is_char_boundary(6)); - /// assert!(s.is_char_boundary(s.len())); - /// - /// // second byte of `ö` - /// assert!(!s.is_char_boundary(2)); - /// - /// // third byte of `老` - /// assert!(!s.is_char_boundary(8)); - /// ``` - #[stable(feature = "is_char_boundary", since = "1.9.0")] - #[inline] - pub fn is_char_boundary(&self, index: usize) -> bool { - core_str::StrExt::is_char_boundary(self, index) - } - - /// Converts a string slice to a byte slice. To convert the byte slice back - /// into a string slice, use the [`str::from_utf8`] function. - /// - /// [`str::from_utf8`]: ./str/fn.from_utf8.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bytes = "bors".as_bytes(); - /// assert_eq!(b"bors", bytes); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline(always)] - pub fn as_bytes(&self) -> &[u8] { - core_str::StrExt::as_bytes(self) - } - - /// Converts a mutable string slice to a mutable byte slice. To convert the - /// mutable byte slice back into a mutable string slice, use the - /// [`str::from_utf8_mut`] function. - /// - /// [`str::from_utf8_mut`]: ./str/fn.from_utf8_mut.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = String::from("Hello"); - /// let bytes = unsafe { s.as_bytes_mut() }; - /// - /// assert_eq!(b"Hello", bytes); - /// ``` - /// - /// Mutability: - /// - /// ``` - /// let mut s = String::from("🗻∈🌏"); - /// - /// unsafe { - /// let bytes = s.as_bytes_mut(); - /// - /// bytes[0] = 0xF0; - /// bytes[1] = 0x9F; - /// bytes[2] = 0x8D; - /// bytes[3] = 0x94; - /// } - /// - /// assert_eq!("🍔∈🌏", s); - /// ``` - #[stable(feature = "str_mut_extras", since = "1.20.0")] - #[inline(always)] - pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - core_str::StrExt::as_bytes_mut(self) - } - - /// Converts a string slice to a raw pointer. - /// - /// As string slices are a slice of bytes, the raw pointer points to a - /// [`u8`]. This pointer will be pointing to the first byte of the string - /// slice. - /// - /// [`u8`]: primitive.u8.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Hello"; - /// let ptr = s.as_ptr(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn as_ptr(&self) -> *const u8 { - core_str::StrExt::as_ptr(self) - } - - /// Returns a subslice of `str`. - /// - /// This is the non-panicking alternative to indexing the `str`. Returns - /// [`None`] whenever equivalent indexing operation would panic. - /// - /// [`None`]: option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// let v = String::from("🗻∈🌏"); - /// - /// assert_eq!(Some("🗻"), v.get(0..4)); - /// - /// // indices not on UTF-8 sequence boundaries - /// assert!(v.get(1..).is_none()); - /// assert!(v.get(..8).is_none()); - /// - /// // out of bounds - /// assert!(v.get(..42).is_none()); - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub fn get>(&self, i: I) -> Option<&I::Output> { - core_str::StrExt::get(self, i) - } - - /// Returns a mutable subslice of `str`. - /// - /// This is the non-panicking alternative to indexing the `str`. Returns - /// [`None`] whenever equivalent indexing operation would panic. - /// - /// [`None`]: option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// let mut v = String::from("hello"); - /// // correct length - /// assert!(v.get_mut(0..5).is_some()); - /// // out of bounds - /// assert!(v.get_mut(..42).is_none()); - /// assert_eq!(Some("he"), v.get_mut(0..2).map(|v| &*v)); - /// - /// assert_eq!("hello", v); - /// { - /// let s = v.get_mut(0..2); - /// let s = s.map(|s| { - /// s.make_ascii_uppercase(); - /// &*s - /// }); - /// assert_eq!(Some("HE"), s); - /// } - /// assert_eq!("HEllo", v); - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - core_str::StrExt::get_mut(self, i) - } - - /// Returns a unchecked subslice of `str`. - /// - /// This is the unchecked alternative to indexing the `str`. - /// - /// # Safety - /// - /// Callers of this function are responsible that these preconditions are - /// satisfied: - /// - /// * The starting index must come before the ending index; - /// * Indexes must be within bounds of the original slice; - /// * Indexes must lie on UTF-8 sequence boundaries. - /// - /// Failing that, the returned string slice may reference invalid memory or - /// violate the invariants communicated by the `str` type. - /// - /// # Examples - /// - /// ``` - /// let v = "🗻∈🌏"; - /// unsafe { - /// assert_eq!("🗻", v.get_unchecked(0..4)); - /// assert_eq!("∈", v.get_unchecked(4..7)); - /// assert_eq!("🌏", v.get_unchecked(7..11)); - /// } - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - core_str::StrExt::get_unchecked(self, i) - } - - /// Returns a mutable, unchecked subslice of `str`. - /// - /// This is the unchecked alternative to indexing the `str`. - /// - /// # Safety - /// - /// Callers of this function are responsible that these preconditions are - /// satisfied: - /// - /// * The starting index must come before the ending index; - /// * Indexes must be within bounds of the original slice; - /// * Indexes must lie on UTF-8 sequence boundaries. - /// - /// Failing that, the returned string slice may reference invalid memory or - /// violate the invariants communicated by the `str` type. - /// - /// # Examples - /// - /// ``` - /// let mut v = String::from("🗻∈🌏"); - /// unsafe { - /// assert_eq!("🗻", v.get_unchecked_mut(0..4)); - /// assert_eq!("∈", v.get_unchecked_mut(4..7)); - /// assert_eq!("🌏", v.get_unchecked_mut(7..11)); - /// } - /// ``` - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - #[inline] - pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - core_str::StrExt::get_unchecked_mut(self, i) - } - - /// Creates a string slice from another string slice, bypassing safety - /// checks. - /// - /// This is generally not recommended, use with caution! For a safe - /// alternative see [`str`] and [`Index`]. - /// - /// [`str`]: primitive.str.html - /// [`Index`]: ops/trait.Index.html - /// - /// This new slice goes from `begin` to `end`, including `begin` but - /// excluding `end`. - /// - /// To get a mutable string slice instead, see the - /// [`slice_mut_unchecked`] method. - /// - /// [`slice_mut_unchecked`]: #method.slice_mut_unchecked - /// - /// # Safety - /// - /// Callers of this function are responsible that three preconditions are - /// satisfied: - /// - /// * `begin` must come before `end`. - /// * `begin` and `end` must be byte positions within the string slice. - /// * `begin` and `end` must lie on UTF-8 sequence boundaries. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// unsafe { - /// assert_eq!("Löwe 老虎 Léopard", s.slice_unchecked(0, 21)); - /// } - /// - /// let s = "Hello, world!"; - /// - /// unsafe { - /// assert_eq!("world", s.slice_unchecked(7, 12)); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - core_str::StrExt::slice_unchecked(self, begin, end) - } - - /// Creates a string slice from another string slice, bypassing safety - /// checks. - /// This is generally not recommended, use with caution! For a safe - /// alternative see [`str`] and [`IndexMut`]. - /// - /// [`str`]: primitive.str.html - /// [`IndexMut`]: ops/trait.IndexMut.html - /// - /// This new slice goes from `begin` to `end`, including `begin` but - /// excluding `end`. - /// - /// To get an immutable string slice instead, see the - /// [`slice_unchecked`] method. - /// - /// [`slice_unchecked`]: #method.slice_unchecked - /// - /// # Safety - /// - /// Callers of this function are responsible that three preconditions are - /// satisfied: - /// - /// * `begin` must come before `end`. - /// * `begin` and `end` must be byte positions within the string slice. - /// * `begin` and `end` must lie on UTF-8 sequence boundaries. - #[stable(feature = "str_slice_mut", since = "1.5.0")] - #[inline] - pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - core_str::StrExt::slice_mut_unchecked(self, begin, end) - } - - /// Divide one string slice into two at an index. - /// - /// The argument, `mid`, should be a byte offset from the start of the - /// string. It must also be on the boundary of a UTF-8 code point. - /// - /// The two slices returned go from the start of the string slice to `mid`, - /// and from `mid` to the end of the string slice. - /// - /// To get mutable string slices instead, see the [`split_at_mut`] - /// method. - /// - /// [`split_at_mut`]: #method.split_at_mut - /// - /// # Panics - /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = "Per Martin-Löf"; - /// - /// let (first, last) = s.split_at(3); - /// - /// assert_eq!("Per", first); - /// assert_eq!(" Martin-Löf", last); - /// ``` - #[inline] - #[stable(feature = "str_split_at", since = "1.4.0")] - pub fn split_at(&self, mid: usize) -> (&str, &str) { - core_str::StrExt::split_at(self, mid) - } - - /// Divide one mutable string slice into two at an index. - /// - /// The argument, `mid`, should be a byte offset from the start of the - /// string. It must also be on the boundary of a UTF-8 code point. - /// - /// The two slices returned go from the start of the string slice to `mid`, - /// and from `mid` to the end of the string slice. - /// - /// To get immutable string slices instead, see the [`split_at`] method. - /// - /// [`split_at`]: #method.split_at - /// - /// # Panics - /// - /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is - /// beyond the last code point of the string slice. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut s = "Per Martin-Löf".to_string(); - /// { - /// let (first, last) = s.split_at_mut(3); - /// first.make_ascii_uppercase(); - /// assert_eq!("PER", first); - /// assert_eq!(" Martin-Löf", last); - /// } - /// assert_eq!("PER Martin-Löf", s); - /// ``` - #[inline] - #[stable(feature = "str_split_at", since = "1.4.0")] - pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - core_str::StrExt::split_at_mut(self, mid) - } - - /// Returns an iterator over the [`char`]s of a string slice. - /// - /// As a string slice consists of valid UTF-8, we can iterate through a - /// string slice by [`char`]. This method returns such an iterator. - /// - /// It's important to remember that [`char`] represents a Unicode Scalar - /// Value, and may not match your idea of what a 'character' is. Iteration - /// over grapheme clusters may be what you actually want. - /// - /// [`char`]: primitive.char.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let word = "goodbye"; - /// - /// let count = word.chars().count(); - /// assert_eq!(7, count); - /// - /// let mut chars = word.chars(); - /// - /// assert_eq!(Some('g'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('o'), chars.next()); - /// assert_eq!(Some('d'), chars.next()); - /// assert_eq!(Some('b'), chars.next()); - /// assert_eq!(Some('y'), chars.next()); - /// assert_eq!(Some('e'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - /// - /// Remember, [`char`]s may not match your human intuition about characters: - /// - /// ``` - /// let y = "y̆"; - /// - /// let mut chars = y.chars(); - /// - /// assert_eq!(Some('y'), chars.next()); // not 'y̆' - /// assert_eq!(Some('\u{0306}'), chars.next()); - /// - /// assert_eq!(None, chars.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn chars(&self) -> Chars { - core_str::StrExt::chars(self) - } - /// Returns an iterator over the [`char`]s of a string slice, and their - /// positions. - /// - /// As a string slice consists of valid UTF-8, we can iterate through a - /// string slice by [`char`]. This method returns an iterator of both - /// these [`char`]s, as well as their byte positions. - /// - /// The iterator yields tuples. The position is first, the [`char`] is - /// second. - /// - /// [`char`]: primitive.char.html - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let word = "goodbye"; - /// - /// let count = word.char_indices().count(); - /// assert_eq!(7, count); - /// - /// let mut char_indices = word.char_indices(); - /// - /// assert_eq!(Some((0, 'g')), char_indices.next()); - /// assert_eq!(Some((1, 'o')), char_indices.next()); - /// assert_eq!(Some((2, 'o')), char_indices.next()); - /// assert_eq!(Some((3, 'd')), char_indices.next()); - /// assert_eq!(Some((4, 'b')), char_indices.next()); - /// assert_eq!(Some((5, 'y')), char_indices.next()); - /// assert_eq!(Some((6, 'e')), char_indices.next()); - /// - /// assert_eq!(None, char_indices.next()); - /// ``` - /// - /// Remember, [`char`]s may not match your human intuition about characters: - /// - /// ``` - /// let yes = "y̆es"; - /// - /// let mut char_indices = yes.char_indices(); - /// - /// assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆') - /// assert_eq!(Some((1, '\u{0306}')), char_indices.next()); - /// - /// // note the 3 here - the last character took up two bytes - /// assert_eq!(Some((3, 'e')), char_indices.next()); - /// assert_eq!(Some((4, 's')), char_indices.next()); - /// - /// assert_eq!(None, char_indices.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn char_indices(&self) -> CharIndices { - core_str::StrExt::char_indices(self) - } - - /// An iterator over the bytes of a string slice. - /// - /// As a string slice consists of a sequence of bytes, we can iterate - /// through a string slice by byte. This method returns such an iterator. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut bytes = "bors".bytes(); - /// - /// assert_eq!(Some(b'b'), bytes.next()); - /// assert_eq!(Some(b'o'), bytes.next()); - /// assert_eq!(Some(b'r'), bytes.next()); - /// assert_eq!(Some(b's'), bytes.next()); - /// - /// assert_eq!(None, bytes.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn bytes(&self) -> Bytes { - core_str::StrExt::bytes(self) - } - - /// Split a string slice by whitespace. - /// - /// The iterator returned will return string slices that are sub-slices of - /// the original string slice, separated by any amount of whitespace. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let mut iter = "A few words".split_whitespace(); - /// - /// assert_eq!(Some("A"), iter.next()); - /// assert_eq!(Some("few"), iter.next()); - /// assert_eq!(Some("words"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - /// - /// All kinds of whitespace are considered: - /// - /// ``` - /// let mut iter = " Mary had\ta\u{2009}little \n\t lamb".split_whitespace(); - /// assert_eq!(Some("Mary"), iter.next()); - /// assert_eq!(Some("had"), iter.next()); - /// assert_eq!(Some("a"), iter.next()); - /// assert_eq!(Some("little"), iter.next()); - /// assert_eq!(Some("lamb"), iter.next()); - /// - /// assert_eq!(None, iter.next()); - /// ``` - #[stable(feature = "split_whitespace", since = "1.1.0")] - #[inline] - pub fn split_whitespace(&self) -> SplitWhitespace { - StrExt::split_whitespace(self) - } - - /// An iterator over the lines of a string, as string slices. - /// - /// Lines are ended with either a newline (`\n`) or a carriage return with - /// a line feed (`\r\n`). - /// - /// The final line ending is optional. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let text = "foo\r\nbar\n\nbaz\n"; - /// let mut lines = text.lines(); - /// - /// assert_eq!(Some("foo"), lines.next()); - /// assert_eq!(Some("bar"), lines.next()); - /// assert_eq!(Some(""), lines.next()); - /// assert_eq!(Some("baz"), lines.next()); - /// - /// assert_eq!(None, lines.next()); - /// ``` - /// - /// The final line ending isn't required: - /// - /// ``` - /// let text = "foo\nbar\n\r\nbaz"; - /// let mut lines = text.lines(); - /// - /// assert_eq!(Some("foo"), lines.next()); - /// assert_eq!(Some("bar"), lines.next()); - /// assert_eq!(Some(""), lines.next()); - /// assert_eq!(Some("baz"), lines.next()); - /// - /// assert_eq!(None, lines.next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn lines(&self) -> Lines { - core_str::StrExt::lines(self) - } - - /// An iterator over the lines of a string. - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.4.0", reason = "use lines() instead now")] - #[inline] - #[allow(deprecated)] - pub fn lines_any(&self) -> LinesAny { - core_str::StrExt::lines_any(self) - } - - /// Returns an iterator of `u16` over the string encoded as UTF-16. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let text = "Zażółć gęślą jaźń"; - /// - /// let utf8_len = text.len(); - /// let utf16_len = text.encode_utf16().count(); - /// - /// assert!(utf16_len <= utf8_len); - /// ``` - #[stable(feature = "encode_utf16", since = "1.8.0")] - pub fn encode_utf16(&self) -> EncodeUtf16 { - EncodeUtf16 { chars: self[..].chars(), extra: 0 } - } - - /// Returns `true` if the given pattern matches a sub-slice of - /// this string slice. - /// - /// Returns `false` if it does not. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.contains("nana")); - /// assert!(!bananas.contains("apples")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - core_str::StrExt::contains(self, pat) - } - - /// Returns `true` if the given pattern matches a prefix of this - /// string slice. - /// - /// Returns `false` if it does not. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.starts_with("bana")); - /// assert!(!bananas.starts_with("nana")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - core_str::StrExt::starts_with(self, pat) - } - - /// Returns `true` if the given pattern matches a suffix of this - /// string slice. - /// - /// Returns `false` if it does not. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let bananas = "bananas"; - /// - /// assert!(bananas.ends_with("anas")); - /// assert!(!bananas.ends_with("nana")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::ends_with(self, pat) - } - - /// Returns the byte index of the first character of this string slice that - /// matches the pattern. - /// - /// Returns [`None`] if the pattern doesn't match. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. - /// - /// [`char`]: primitive.char.html - /// [`None`]: option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.find('L'), Some(0)); - /// assert_eq!(s.find('é'), Some(14)); - /// assert_eq!(s.find("Léopard"), Some(13)); - /// ``` - /// - /// More complex patterns using point-free style and closures: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.find(char::is_whitespace), Some(5)); - /// assert_eq!(s.find(char::is_lowercase), Some(1)); - /// assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1)); - /// assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4)); - /// ``` - /// - /// Not finding the pattern: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// let x: &[_] = &['1', '2']; - /// - /// assert_eq!(s.find(x), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - core_str::StrExt::find(self, pat) - } - - /// Returns the byte index of the last character of this string slice that - /// matches the pattern. - /// - /// Returns [`None`] if the pattern doesn't match. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. - /// - /// [`char`]: primitive.char.html - /// [`None`]: option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.rfind('L'), Some(13)); - /// assert_eq!(s.rfind('é'), Some(14)); - /// ``` - /// - /// More complex patterns with closures: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// - /// assert_eq!(s.rfind(char::is_whitespace), Some(12)); - /// assert_eq!(s.rfind(char::is_lowercase), Some(20)); - /// ``` - /// - /// Not finding the pattern: - /// - /// ``` - /// let s = "Löwe 老虎 Léopard"; - /// let x: &[_] = &['1', '2']; - /// - /// assert_eq!(s.rfind(x), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rfind(self, pat) - } - - /// An iterator over substrings of this string slice, separated by - /// characters matched by a pattern. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines the - /// split. - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, eg, [`char`] but not for `&str`. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rsplit`] method can be used. - /// - /// [`char`]: primitive.char.html - /// [`rsplit`]: #method.rsplit - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect(); - /// assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]); - /// - /// let v: Vec<&str> = "".split('X').collect(); - /// assert_eq!(v, [""]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".split('X').collect(); - /// assert_eq!(v, ["lion", "", "tiger", "leopard"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".split("::").collect(); - /// assert_eq!(v, ["lion", "tiger", "leopard"]); - /// - /// let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect(); - /// assert_eq!(v, ["abc", "def", "ghi"]); - /// - /// let v: Vec<&str> = "lionXtigerXleopard".split(char::is_uppercase).collect(); - /// assert_eq!(v, ["lion", "tiger", "leopard"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".split(|c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["abc", "def", "ghi"]); - /// ``` - /// - /// If a string contains multiple contiguous separators, you will end up - /// with empty strings in the output: - /// - /// ``` - /// let x = "||||a||b|c".to_string(); - /// let d: Vec<_> = x.split('|').collect(); - /// - /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); - /// ``` - /// - /// Contiguous separators are separated by the empty string. - /// - /// ``` - /// let x = "(///)".to_string(); - /// let d: Vec<_> = x.split('/').collect(); - /// - /// assert_eq!(d, &["(", "", "", ")"]); - /// ``` - /// - /// Separators at the start or end of a string are neighbored - /// by empty strings. - /// - /// ``` - /// let d: Vec<_> = "010".split("0").collect(); - /// assert_eq!(d, &["", "1", ""]); - /// ``` - /// - /// When the empty string is used as a separator, it separates - /// every character in the string, along with the beginning - /// and end of the string. - /// - /// ``` - /// let f: Vec<_> = "rust".split("").collect(); - /// assert_eq!(f, &["", "r", "u", "s", "t", ""]); - /// ``` - /// - /// Contiguous separators can lead to possibly surprising behavior - /// when whitespace is used as the separator. This code is correct: - /// - /// ``` - /// let x = " a b c".to_string(); - /// let d: Vec<_> = x.split(' ').collect(); - /// - /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); - /// ``` - /// - /// It does _not_ give you: - /// - /// ```,ignore - /// assert_eq!(d, &["a", "b", "c"]); - /// ``` - /// - /// Use [`split_whitespace`] for this behavior. - /// - /// [`split_whitespace`]: #method.split_whitespace - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - core_str::StrExt::split(self, pat) - } - - /// An iterator over substrings of the given string slice, separated by - /// characters matched by a pattern and yielded in reverse order. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines the - /// split. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// - /// For iterating from the front, the [`split`] method can be used. - /// - /// [`split`]: #method.split - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect(); - /// assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]); - /// - /// let v: Vec<&str> = "".rsplit('X').collect(); - /// assert_eq!(v, [""]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".rsplit('X').collect(); - /// assert_eq!(v, ["leopard", "tiger", "", "lion"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect(); - /// assert_eq!(v, ["leopard", "tiger", "lion"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".rsplit(|c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["ghi", "def", "abc"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rsplit(self, pat) - } - - /// An iterator over substrings of the given string slice, separated by - /// characters matched by a pattern. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines the - /// split. - /// - /// Equivalent to [`split`], except that the trailing substring - /// is skipped if empty. - /// - /// [`split`]: #method.split - /// - /// This method can be used for string data that is _terminated_, - /// rather than _separated_ by a pattern. - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, eg, [`char`] but not for `&str`. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// [`char`]: primitive.char.html - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rsplit_terminator`] method can be used. - /// - /// [`rsplit_terminator`]: #method.rsplit_terminator - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "A.B.".split_terminator('.').collect(); - /// assert_eq!(v, ["A", "B"]); - /// - /// let v: Vec<&str> = "A..B..".split_terminator(".").collect(); - /// assert_eq!(v, ["A", "", "B", ""]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - core_str::StrExt::split_terminator(self, pat) - } - - /// An iterator over substrings of `self`, separated by characters - /// matched by a pattern and yielded in reverse order. - /// - /// The pattern can be a simple `&str`, [`char`], or a closure that - /// determines the split. - /// Additional libraries might provide more complex patterns like - /// regular expressions. - /// - /// [`char`]: primitive.char.html - /// - /// Equivalent to [`split`], except that the trailing substring is - /// skipped if empty. - /// - /// [`split`]: #method.split - /// - /// This method can be used for string data that is _terminated_, - /// rather than _separated_ by a pattern. - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a - /// reverse search, and it will be double ended if a forward/reverse - /// search yields the same elements. - /// - /// For iterating from the front, the [`split_terminator`] method can be - /// used. - /// - /// [`split_terminator`]: #method.split_terminator - /// - /// # Examples - /// - /// ``` - /// let v: Vec<&str> = "A.B.".rsplit_terminator('.').collect(); - /// assert_eq!(v, ["B", "A"]); - /// - /// let v: Vec<&str> = "A..B..".rsplit_terminator(".").collect(); - /// assert_eq!(v, ["", "B", "", "A"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rsplit_terminator(self, pat) - } - - /// An iterator over substrings of the given string slice, separated by a - /// pattern, restricted to returning at most `n` items. - /// - /// If `n` substrings are returned, the last substring (the `n`th substring) - /// will contain the remainder of the string. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines the - /// split. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator will not be double ended, because it is - /// not efficient to support. - /// - /// If the pattern allows a reverse search, the [`rsplitn`] method can be - /// used. - /// - /// [`rsplitn`]: #method.rsplitn - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lambda".splitn(3, ' ').collect(); - /// assert_eq!(v, ["Mary", "had", "a little lambda"]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".splitn(3, "X").collect(); - /// assert_eq!(v, ["lion", "", "tigerXleopard"]); - /// - /// let v: Vec<&str> = "abcXdef".splitn(1, 'X').collect(); - /// assert_eq!(v, ["abcXdef"]); - /// - /// let v: Vec<&str> = "".splitn(1, 'X').collect(); - /// assert_eq!(v, [""]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".splitn(2, |c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["abc", "defXghi"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { - core_str::StrExt::splitn(self, n, pat) - } - - /// An iterator over substrings of this string slice, separated by a - /// pattern, starting from the end of the string, restricted to returning - /// at most `n` items. - /// - /// If `n` substrings are returned, the last substring (the `n`th substring) - /// will contain the remainder of the string. - /// - /// The pattern can be a `&str`, [`char`], or a closure that - /// determines the split. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator will not be double ended, because it is not - /// efficient to support. - /// - /// For splitting from the front, the [`splitn`] method can be used. - /// - /// [`splitn`]: #method.splitn - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// let v: Vec<&str> = "Mary had a little lamb".rsplitn(3, ' ').collect(); - /// assert_eq!(v, ["lamb", "little", "Mary had a"]); - /// - /// let v: Vec<&str> = "lionXXtigerXleopard".rsplitn(3, 'X').collect(); - /// assert_eq!(v, ["leopard", "tiger", "lionX"]); - /// - /// let v: Vec<&str> = "lion::tiger::leopard".rsplitn(2, "::").collect(); - /// assert_eq!(v, ["leopard", "lion::tiger"]); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// let v: Vec<&str> = "abc1defXghi".rsplitn(2, |c| c == '1' || c == 'X').collect(); - /// assert_eq!(v, ["ghi", "abc1def"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rsplitn(self, n, pat) - } - - /// An iterator over the disjoint matches of a pattern within the given string - /// slice. - /// - /// The pattern can be a `&str`, [`char`], or a closure that - /// determines if a character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, eg, [`char`] but not for `&str`. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// [`char`]: primitive.char.html - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rmatches`] method can be used. - /// - /// [`rmatches`]: #method.rmatches - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect(); - /// assert_eq!(v, ["abc", "abc", "abc"]); - /// - /// let v: Vec<&str> = "1abc2abc3".matches(char::is_numeric).collect(); - /// assert_eq!(v, ["1", "2", "3"]); - /// ``` - #[stable(feature = "str_matches", since = "1.2.0")] - #[inline] - pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - core_str::StrExt::matches(self, pat) - } + let len = self.iter().map(|s| s.borrow().len()).sum(); + let mut result = String::with_capacity(len); - /// An iterator over the disjoint matches of a pattern within this string slice, - /// yielded in reverse order. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// - /// For iterating from the front, the [`matches`] method can be used. - /// - /// [`matches`]: #method.matches - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect(); - /// assert_eq!(v, ["abc", "abc", "abc"]); - /// - /// let v: Vec<&str> = "1abc2abc3".rmatches(char::is_numeric).collect(); - /// assert_eq!(v, ["3", "2", "1"]); - /// ``` - #[stable(feature = "str_matches", since = "1.2.0")] - #[inline] - pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rmatches(self, pat) - } + for s in self { + result.push_str(s.borrow()) + } - /// An iterator over the disjoint matches of a pattern within this string - /// slice as well as the index that the match starts at. - /// - /// For matches of `pat` within `self` that overlap, only the indices - /// corresponding to the first match are returned. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines - /// if a character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern - /// allows a reverse search and forward/reverse search yields the same - /// elements. This is true for, eg, [`char`] but not for `&str`. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// - /// If the pattern allows a reverse search but its results might differ - /// from a forward search, the [`rmatch_indices`] method can be used. - /// - /// [`rmatch_indices`]: #method.rmatch_indices - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect(); - /// assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]); - /// - /// let v: Vec<_> = "1abcabc2".match_indices("abc").collect(); - /// assert_eq!(v, [(1, "abc"), (4, "abc")]); - /// - /// let v: Vec<_> = "ababa".match_indices("aba").collect(); - /// assert_eq!(v, [(0, "aba")]); // only the first `aba` - /// ``` - #[stable(feature = "str_match_indices", since = "1.5.0")] - #[inline] - pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - core_str::StrExt::match_indices(self, pat) + result } - /// An iterator over the disjoint matches of a pattern within `self`, - /// yielded in reverse order along with the index of the match. - /// - /// For matches of `pat` within `self` that overlap, only the indices - /// corresponding to the last match are returned. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if a - /// character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Iterator behavior - /// - /// The returned iterator requires that the pattern supports a reverse - /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse - /// search yields the same elements. - /// - /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html - /// - /// For iterating from the front, the [`match_indices`] method can be used. - /// - /// [`match_indices`]: #method.match_indices - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect(); - /// assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]); - /// - /// let v: Vec<_> = "1abcabc2".rmatch_indices("abc").collect(); - /// assert_eq!(v, [(4, "abc"), (1, "abc")]); - /// - /// let v: Vec<_> = "ababa".rmatch_indices("aba").collect(); - /// assert_eq!(v, [(2, "aba")]); // only the last `aba` - /// ``` - #[stable(feature = "str_match_indices", since = "1.5.0")] - #[inline] - pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::rmatch_indices(self, pat) - } + fn join(&self, sep: &str) -> String { + if self.is_empty() { + return String::new(); + } - /// Returns a string slice with leading and trailing whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!("Hello\tworld", s.trim()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim(&self) -> &str { - StrExt::trim(self) - } + // concat is faster + if sep.is_empty() { + return self.concat(); + } - /// Returns a string slice with leading whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Left' in this context means the first - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _right_ side, not the left. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!("Hello\tworld\t", s.trim_left()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = " English"; - /// assert!(Some('E') == s.trim_left().chars().next()); - /// - /// let s = " עברית"; - /// assert!(Some('ע') == s.trim_left().chars().next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_left(&self) -> &str { - StrExt::trim_left(self) - } + // this is wrong without the guarantee that `self` is non-empty + // `len` calculation may overflow but push_str but will check boundaries + let len = sep.len() * (self.len() - 1) + + self.iter().map(|s| s.borrow().len()).sum::(); + let mut result = String::with_capacity(len); + let mut first = true; - /// Returns a string slice with trailing whitespace removed. - /// - /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Right' in this context means the last - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _left_ side, not the right. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// let s = " Hello\tworld\t"; - /// - /// assert_eq!(" Hello\tworld", s.trim_right()); - /// ``` - /// - /// Directionality: - /// - /// ``` - /// let s = "English "; - /// assert!(Some('h') == s.trim_right().chars().rev().next()); - /// - /// let s = "עברית "; - /// assert!(Some('ת') == s.trim_right().chars().rev().next()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_right(&self) -> &str { - StrExt::trim_right(self) + for s in self { + if first { + first = false; + } else { + result.push_str(sep); + } + result.push_str(s.borrow()); + } + result } - /// Returns a string slice with all prefixes and suffixes that match a - /// pattern repeatedly removed. - /// - /// The pattern can be a [`char`] or a closure that determines if a - /// character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar"); - /// assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_matches(x), "foo1bar"); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// assert_eq!("1foo1barXX".trim_matches(|c| c == '1' || c == 'X'), "foo1bar"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a> - { - core_str::StrExt::trim_matches(self, pat) + fn connect(&self, sep: &str) -> String { + self.join(sep) } +} - /// Returns a string slice with all prefixes that match a pattern - /// repeatedly removed. - /// - /// The pattern can be a `&str`, [`char`], or a closure that determines if - /// a character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Left' in this context means the first - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _right_ side, not the left. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); - /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - core_str::StrExt::trim_left_matches(self, pat) +#[stable(feature = "rust1", since = "1.0.0")] +impl Borrow for String { + #[inline] + fn borrow(&self) -> &str { + &self[..] } +} - /// Returns a string slice with all suffixes that match a pattern - /// repeatedly removed. - /// - /// The pattern can be a `&str`, [`char`], or a closure that - /// determines if a character matches. - /// - /// [`char`]: primitive.char.html - /// - /// # Text directionality - /// - /// A string is a sequence of bytes. 'Right' in this context means the last - /// position of that byte string; for a language like Arabic or Hebrew - /// which are 'right to left' rather than 'left to right', this will be - /// the _left_ side, not the right. - /// - /// # Examples - /// - /// Simple patterns: - /// - /// ``` - /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); - /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); - /// - /// let x: &[_] = &['1', '2']; - /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); - /// ``` - /// - /// A more complex pattern, using a closure: - /// - /// ``` - /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a> - { - core_str::StrExt::trim_right_matches(self, pat) +#[stable(feature = "rust1", since = "1.0.0")] +impl ToOwned for str { + type Owned = String; + fn to_owned(&self) -> String { + unsafe { String::from_utf8_unchecked(self.as_bytes().to_owned()) } } - /// Parses this string slice into another type. - /// - /// Because `parse` is so general, it can cause problems with type - /// inference. As such, `parse` is one of the few times you'll see - /// the syntax affectionately known as the 'turbofish': `::<>`. This - /// helps the inference algorithm understand specifically which type - /// you're trying to parse into. - /// - /// `parse` can parse any type that implements the [`FromStr`] trait. - /// - /// [`FromStr`]: str/trait.FromStr.html - /// - /// # Errors - /// - /// Will return [`Err`] if it's not possible to parse this string slice into - /// the desired type. - /// - /// [`Err`]: str/trait.FromStr.html#associatedtype.Err - /// - /// # Examples - /// - /// Basic usage - /// - /// ``` - /// let four: u32 = "4".parse().unwrap(); - /// - /// assert_eq!(4, four); - /// ``` - /// - /// Using the 'turbofish' instead of annotating `four`: - /// - /// ``` - /// let four = "4".parse::(); - /// - /// assert_eq!(Ok(4), four); - /// ``` - /// - /// Failing to parse: - /// - /// ``` - /// let nope = "j".parse::(); - /// - /// assert!(nope.is_err()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn parse(&self) -> Result { - core_str::StrExt::parse(self) + fn clone_into(&self, target: &mut String) { + let mut b = mem::replace(target, String::new()).into_bytes(); + self.as_bytes().clone_into(&mut b); + *target = unsafe { String::from_utf8_unchecked(b) } } +} + +/// Methods for string slices. +#[cfg_attr(stage0, lang = "str")] +#[cfg_attr(not(stage0), lang = "str_alloc")] +#[cfg(not(test))] +impl str { + #[cfg(stage0)] + str_core_methods!(); /// Converts a `Box` into a `Box<[u8]>` without copying or allocating. /// @@ -2140,26 +491,6 @@ impl str { unsafe { String::from_utf8_unchecked(buf) } } - /// Checks if all characters in this string are within the ASCII range. - /// - /// # Examples - /// - /// ``` - /// let ascii = "hello!\n"; - /// let non_ascii = "Grüße, Jürgen ❤"; - /// - /// assert!(ascii.is_ascii()); - /// assert!(!non_ascii.is_ascii()); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn is_ascii(&self) -> bool { - // We can treat each byte as character here: all multibyte characters - // start with a byte that is not in the ascii range, so we will stop - // there already. - self.bytes().all(|b| b.is_ascii()) - } - /// Returns a copy of this string where each character is mapped to its /// ASCII upper case equivalent. /// @@ -2219,54 +550,6 @@ impl str { // make_ascii_lowercase() preserves the UTF-8 invariant. unsafe { String::from_utf8_unchecked(bytes) } } - - /// Checks that two strings are an ASCII case-insensitive match. - /// - /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, - /// but without allocating and copying temporaries. - /// - /// # Examples - /// - /// ``` - /// assert!("Ferris".eq_ignore_ascii_case("FERRIS")); - /// assert!("Ferrös".eq_ignore_ascii_case("FERRöS")); - /// assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS")); - /// ``` - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - #[inline] - pub fn eq_ignore_ascii_case(&self, other: &str) -> bool { - self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) - } - - /// Converts this string to its ASCII upper case equivalent in-place. - /// - /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new uppercased value without modifying the existing one, use - /// [`to_ascii_uppercase`]. - /// - /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - pub fn make_ascii_uppercase(&mut self) { - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_uppercase() - } - - /// Converts this string to its ASCII lower case equivalent in-place. - /// - /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', - /// but non-ASCII letters are unchanged. - /// - /// To return a new lowercased value without modifying the existing one, use - /// [`to_ascii_lowercase`]. - /// - /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase - #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] - pub fn make_ascii_lowercase(&mut self) { - let me = unsafe { self.as_bytes_mut() }; - me.make_ascii_lowercase() - } } /// Converts a boxed slice of bytes to a boxed string slice without checking diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index f4fafe304c0..5a107951b0b 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -93,6 +93,7 @@ #![feature(rustc_const_unstable)] #![feature(simd_ffi)] #![feature(core_slice_ext)] +#![feature(core_str_ext)] #![feature(specialization)] #![feature(staged_api)] #![feature(stmt_expr_attributes)] diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index 32c1531bdc0..8212648f2d8 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -62,4 +62,5 @@ pub use result::Result::{self, Ok, Err}; pub use slice::SliceExt; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] +#[cfg(stage0)] pub use str::StrExt; diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index f1fe23092de..d33eaace79d 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2218,10 +2218,6 @@ pub trait StrExt { fn parse(&self) -> Result; #[stable(feature = "split_whitespace", since = "1.1.0")] fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; - #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] - fn is_whitespace(&self) -> bool; - #[stable(feature = "unicode_methods_on_intrinsics", since = "1.27.0")] - fn is_alphanumeric(&self) -> bool; #[stable(feature = "rust1", since = "1.0.0")] fn trim(&self) -> &str; #[stable(feature = "rust1", since = "1.0.0")] @@ -2555,31 +2551,1700 @@ impl StrExt for str { } #[inline] - fn is_whitespace(&self) -> bool { - self.chars().all(|c| c.is_whitespace()) + fn trim(&self) -> &str { + self.trim_matches(|c: char| c.is_whitespace()) + } + + #[inline] + fn trim_left(&self) -> &str { + self.trim_left_matches(|c: char| c.is_whitespace()) + } + + #[inline] + fn trim_right(&self) -> &str { + self.trim_right_matches(|c: char| c.is_whitespace()) + } +} + +// FIXME: remove (inline) this macro and the SliceExt trait +// when updating to a bootstrap compiler that has the new lang items. +#[cfg_attr(stage0, macro_export)] +#[unstable(feature = "core_str_ext", issue = "32110")] +macro_rules! str_core_methods { () => { + /// Returns the length of `self`. + /// + /// This length is in bytes, not [`char`]s or graphemes. In other words, + /// it may not be what a human considers the length of the string. + /// + /// [`char`]: primitive.char.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let len = "foo".len(); + /// assert_eq!(3, len); + /// + /// let len = "ƒoo".len(); // fancy f! + /// assert_eq!(4, len); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn len(&self) -> usize { + StrExt::len(self) + } + + /// Returns `true` if `self` has a length of zero bytes. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = ""; + /// assert!(s.is_empty()); + /// + /// let s = "not empty"; + /// assert!(!s.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + StrExt::is_empty(self) + } + + /// Checks that `index`-th byte lies at the start and/or end of a + /// UTF-8 code point sequence. + /// + /// The start and end of the string (when `index == self.len()`) are + /// considered to be + /// boundaries. + /// + /// Returns `false` if `index` is greater than `self.len()`. + /// + /// # Examples + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// assert!(s.is_char_boundary(0)); + /// // start of `老` + /// assert!(s.is_char_boundary(6)); + /// assert!(s.is_char_boundary(s.len())); + /// + /// // second byte of `ö` + /// assert!(!s.is_char_boundary(2)); + /// + /// // third byte of `老` + /// assert!(!s.is_char_boundary(8)); + /// ``` + #[stable(feature = "is_char_boundary", since = "1.9.0")] + #[inline] + pub fn is_char_boundary(&self, index: usize) -> bool { + StrExt::is_char_boundary(self, index) + } + + /// Converts a string slice to a byte slice. To convert the byte slice back + /// into a string slice, use the [`str::from_utf8`] function. + /// + /// [`str::from_utf8`]: ./str/fn.from_utf8.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bytes = "bors".as_bytes(); + /// assert_eq!(b"bors", bytes); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline(always)] + pub fn as_bytes(&self) -> &[u8] { + StrExt::as_bytes(self) + } + + /// Converts a mutable string slice to a mutable byte slice. To convert the + /// mutable byte slice back into a mutable string slice, use the + /// [`str::from_utf8_mut`] function. + /// + /// [`str::from_utf8_mut`]: ./str/fn.from_utf8_mut.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = String::from("Hello"); + /// let bytes = unsafe { s.as_bytes_mut() }; + /// + /// assert_eq!(b"Hello", bytes); + /// ``` + /// + /// Mutability: + /// + /// ``` + /// let mut s = String::from("🗻∈🌏"); + /// + /// unsafe { + /// let bytes = s.as_bytes_mut(); + /// + /// bytes[0] = 0xF0; + /// bytes[1] = 0x9F; + /// bytes[2] = 0x8D; + /// bytes[3] = 0x94; + /// } + /// + /// assert_eq!("🍔∈🌏", s); + /// ``` + #[stable(feature = "str_mut_extras", since = "1.20.0")] + #[inline(always)] + pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { + StrExt::as_bytes_mut(self) + } + + /// Converts a string slice to a raw pointer. + /// + /// As string slices are a slice of bytes, the raw pointer points to a + /// [`u8`]. This pointer will be pointing to the first byte of the string + /// slice. + /// + /// [`u8`]: primitive.u8.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Hello"; + /// let ptr = s.as_ptr(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn as_ptr(&self) -> *const u8 { + StrExt::as_ptr(self) + } + + /// Returns a subslice of `str`. + /// + /// This is the non-panicking alternative to indexing the `str`. Returns + /// [`None`] whenever equivalent indexing operation would panic. + /// + /// [`None`]: option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// let v = String::from("🗻∈🌏"); + /// + /// assert_eq!(Some("🗻"), v.get(0..4)); + /// + /// // indices not on UTF-8 sequence boundaries + /// assert!(v.get(1..).is_none()); + /// assert!(v.get(..8).is_none()); + /// + /// // out of bounds + /// assert!(v.get(..42).is_none()); + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub fn get>(&self, i: I) -> Option<&I::Output> { + StrExt::get(self, i) + } + + /// Returns a mutable subslice of `str`. + /// + /// This is the non-panicking alternative to indexing the `str`. Returns + /// [`None`] whenever equivalent indexing operation would panic. + /// + /// [`None`]: option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// let mut v = String::from("hello"); + /// // correct length + /// assert!(v.get_mut(0..5).is_some()); + /// // out of bounds + /// assert!(v.get_mut(..42).is_none()); + /// assert_eq!(Some("he"), v.get_mut(0..2).map(|v| &*v)); + /// + /// assert_eq!("hello", v); + /// { + /// let s = v.get_mut(0..2); + /// let s = s.map(|s| { + /// s.make_ascii_uppercase(); + /// &*s + /// }); + /// assert_eq!(Some("HE"), s); + /// } + /// assert_eq!("HEllo", v); + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { + StrExt::get_mut(self, i) + } + + /// Returns a unchecked subslice of `str`. + /// + /// This is the unchecked alternative to indexing the `str`. + /// + /// # Safety + /// + /// Callers of this function are responsible that these preconditions are + /// satisfied: + /// + /// * The starting index must come before the ending index; + /// * Indexes must be within bounds of the original slice; + /// * Indexes must lie on UTF-8 sequence boundaries. + /// + /// Failing that, the returned string slice may reference invalid memory or + /// violate the invariants communicated by the `str` type. + /// + /// # Examples + /// + /// ``` + /// let v = "🗻∈🌏"; + /// unsafe { + /// assert_eq!("🗻", v.get_unchecked(0..4)); + /// assert_eq!("∈", v.get_unchecked(4..7)); + /// assert_eq!("🌏", v.get_unchecked(7..11)); + /// } + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { + StrExt::get_unchecked(self, i) + } + + /// Returns a mutable, unchecked subslice of `str`. + /// + /// This is the unchecked alternative to indexing the `str`. + /// + /// # Safety + /// + /// Callers of this function are responsible that these preconditions are + /// satisfied: + /// + /// * The starting index must come before the ending index; + /// * Indexes must be within bounds of the original slice; + /// * Indexes must lie on UTF-8 sequence boundaries. + /// + /// Failing that, the returned string slice may reference invalid memory or + /// violate the invariants communicated by the `str` type. + /// + /// # Examples + /// + /// ``` + /// let mut v = String::from("🗻∈🌏"); + /// unsafe { + /// assert_eq!("🗻", v.get_unchecked_mut(0..4)); + /// assert_eq!("∈", v.get_unchecked_mut(4..7)); + /// assert_eq!("🌏", v.get_unchecked_mut(7..11)); + /// } + /// ``` + #[stable(feature = "str_checked_slicing", since = "1.20.0")] + #[inline] + pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { + StrExt::get_unchecked_mut(self, i) + } + + /// Creates a string slice from another string slice, bypassing safety + /// checks. + /// + /// This is generally not recommended, use with caution! For a safe + /// alternative see [`str`] and [`Index`]. + /// + /// [`str`]: primitive.str.html + /// [`Index`]: ops/trait.Index.html + /// + /// This new slice goes from `begin` to `end`, including `begin` but + /// excluding `end`. + /// + /// To get a mutable string slice instead, see the + /// [`slice_mut_unchecked`] method. + /// + /// [`slice_mut_unchecked`]: #method.slice_mut_unchecked + /// + /// # Safety + /// + /// Callers of this function are responsible that three preconditions are + /// satisfied: + /// + /// * `begin` must come before `end`. + /// * `begin` and `end` must be byte positions within the string slice. + /// * `begin` and `end` must lie on UTF-8 sequence boundaries. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// unsafe { + /// assert_eq!("Löwe 老虎 Léopard", s.slice_unchecked(0, 21)); + /// } + /// + /// let s = "Hello, world!"; + /// + /// unsafe { + /// assert_eq!("world", s.slice_unchecked(7, 12)); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { + StrExt::slice_unchecked(self, begin, end) + } + + /// Creates a string slice from another string slice, bypassing safety + /// checks. + /// This is generally not recommended, use with caution! For a safe + /// alternative see [`str`] and [`IndexMut`]. + /// + /// [`str`]: primitive.str.html + /// [`IndexMut`]: ops/trait.IndexMut.html + /// + /// This new slice goes from `begin` to `end`, including `begin` but + /// excluding `end`. + /// + /// To get an immutable string slice instead, see the + /// [`slice_unchecked`] method. + /// + /// [`slice_unchecked`]: #method.slice_unchecked + /// + /// # Safety + /// + /// Callers of this function are responsible that three preconditions are + /// satisfied: + /// + /// * `begin` must come before `end`. + /// * `begin` and `end` must be byte positions within the string slice. + /// * `begin` and `end` must lie on UTF-8 sequence boundaries. + #[stable(feature = "str_slice_mut", since = "1.5.0")] + #[inline] + pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { + StrExt::slice_mut_unchecked(self, begin, end) + } + + /// Divide one string slice into two at an index. + /// + /// The argument, `mid`, should be a byte offset from the start of the + /// string. It must also be on the boundary of a UTF-8 code point. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get mutable string slices instead, see the [`split_at_mut`] + /// method. + /// + /// [`split_at_mut`]: #method.split_at_mut + /// + /// # Panics + /// + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is + /// beyond the last code point of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = "Per Martin-Löf"; + /// + /// let (first, last) = s.split_at(3); + /// + /// assert_eq!("Per", first); + /// assert_eq!(" Martin-Löf", last); + /// ``` + #[inline] + #[stable(feature = "str_split_at", since = "1.4.0")] + pub fn split_at(&self, mid: usize) -> (&str, &str) { + StrExt::split_at(self, mid) + } + + /// Divide one mutable string slice into two at an index. + /// + /// The argument, `mid`, should be a byte offset from the start of the + /// string. It must also be on the boundary of a UTF-8 code point. + /// + /// The two slices returned go from the start of the string slice to `mid`, + /// and from `mid` to the end of the string slice. + /// + /// To get immutable string slices instead, see the [`split_at`] method. + /// + /// [`split_at`]: #method.split_at + /// + /// # Panics + /// + /// Panics if `mid` is not on a UTF-8 code point boundary, or if it is + /// beyond the last code point of the string slice. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut s = "Per Martin-Löf".to_string(); + /// { + /// let (first, last) = s.split_at_mut(3); + /// first.make_ascii_uppercase(); + /// assert_eq!("PER", first); + /// assert_eq!(" Martin-Löf", last); + /// } + /// assert_eq!("PER Martin-Löf", s); + /// ``` + #[inline] + #[stable(feature = "str_split_at", since = "1.4.0")] + pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { + StrExt::split_at_mut(self, mid) + } + + /// Returns an iterator over the [`char`]s of a string slice. + /// + /// As a string slice consists of valid UTF-8, we can iterate through a + /// string slice by [`char`]. This method returns such an iterator. + /// + /// It's important to remember that [`char`] represents a Unicode Scalar + /// Value, and may not match your idea of what a 'character' is. Iteration + /// over grapheme clusters may be what you actually want. + /// + /// [`char`]: primitive.char.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let word = "goodbye"; + /// + /// let count = word.chars().count(); + /// assert_eq!(7, count); + /// + /// let mut chars = word.chars(); + /// + /// assert_eq!(Some('g'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('o'), chars.next()); + /// assert_eq!(Some('d'), chars.next()); + /// assert_eq!(Some('b'), chars.next()); + /// assert_eq!(Some('y'), chars.next()); + /// assert_eq!(Some('e'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + /// + /// Remember, [`char`]s may not match your human intuition about characters: + /// + /// ``` + /// let y = "y̆"; + /// + /// let mut chars = y.chars(); + /// + /// assert_eq!(Some('y'), chars.next()); // not 'y̆' + /// assert_eq!(Some('\u{0306}'), chars.next()); + /// + /// assert_eq!(None, chars.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn chars(&self) -> Chars { + StrExt::chars(self) + } + /// Returns an iterator over the [`char`]s of a string slice, and their + /// positions. + /// + /// As a string slice consists of valid UTF-8, we can iterate through a + /// string slice by [`char`]. This method returns an iterator of both + /// these [`char`]s, as well as their byte positions. + /// + /// The iterator yields tuples. The position is first, the [`char`] is + /// second. + /// + /// [`char`]: primitive.char.html + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let word = "goodbye"; + /// + /// let count = word.char_indices().count(); + /// assert_eq!(7, count); + /// + /// let mut char_indices = word.char_indices(); + /// + /// assert_eq!(Some((0, 'g')), char_indices.next()); + /// assert_eq!(Some((1, 'o')), char_indices.next()); + /// assert_eq!(Some((2, 'o')), char_indices.next()); + /// assert_eq!(Some((3, 'd')), char_indices.next()); + /// assert_eq!(Some((4, 'b')), char_indices.next()); + /// assert_eq!(Some((5, 'y')), char_indices.next()); + /// assert_eq!(Some((6, 'e')), char_indices.next()); + /// + /// assert_eq!(None, char_indices.next()); + /// ``` + /// + /// Remember, [`char`]s may not match your human intuition about characters: + /// + /// ``` + /// let yes = "y̆es"; + /// + /// let mut char_indices = yes.char_indices(); + /// + /// assert_eq!(Some((0, 'y')), char_indices.next()); // not (0, 'y̆') + /// assert_eq!(Some((1, '\u{0306}')), char_indices.next()); + /// + /// // note the 3 here - the last character took up two bytes + /// assert_eq!(Some((3, 'e')), char_indices.next()); + /// assert_eq!(Some((4, 's')), char_indices.next()); + /// + /// assert_eq!(None, char_indices.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn char_indices(&self) -> CharIndices { + StrExt::char_indices(self) + } + + /// An iterator over the bytes of a string slice. + /// + /// As a string slice consists of a sequence of bytes, we can iterate + /// through a string slice by byte. This method returns such an iterator. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut bytes = "bors".bytes(); + /// + /// assert_eq!(Some(b'b'), bytes.next()); + /// assert_eq!(Some(b'o'), bytes.next()); + /// assert_eq!(Some(b'r'), bytes.next()); + /// assert_eq!(Some(b's'), bytes.next()); + /// + /// assert_eq!(None, bytes.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn bytes(&self) -> Bytes { + StrExt::bytes(self) + } + + /// Split a string slice by whitespace. + /// + /// The iterator returned will return string slices that are sub-slices of + /// the original string slice, separated by any amount of whitespace. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let mut iter = "A few words".split_whitespace(); + /// + /// assert_eq!(Some("A"), iter.next()); + /// assert_eq!(Some("few"), iter.next()); + /// assert_eq!(Some("words"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// All kinds of whitespace are considered: + /// + /// ``` + /// let mut iter = " Mary had\ta\u{2009}little \n\t lamb".split_whitespace(); + /// assert_eq!(Some("Mary"), iter.next()); + /// assert_eq!(Some("had"), iter.next()); + /// assert_eq!(Some("a"), iter.next()); + /// assert_eq!(Some("little"), iter.next()); + /// assert_eq!(Some("lamb"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + #[stable(feature = "split_whitespace", since = "1.1.0")] + #[inline] + pub fn split_whitespace(&self) -> SplitWhitespace { + StrExt::split_whitespace(self) + } + + /// An iterator over the lines of a string, as string slices. + /// + /// Lines are ended with either a newline (`\n`) or a carriage return with + /// a line feed (`\r\n`). + /// + /// The final line ending is optional. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let text = "foo\r\nbar\n\nbaz\n"; + /// let mut lines = text.lines(); + /// + /// assert_eq!(Some("foo"), lines.next()); + /// assert_eq!(Some("bar"), lines.next()); + /// assert_eq!(Some(""), lines.next()); + /// assert_eq!(Some("baz"), lines.next()); + /// + /// assert_eq!(None, lines.next()); + /// ``` + /// + /// The final line ending isn't required: + /// + /// ``` + /// let text = "foo\nbar\n\r\nbaz"; + /// let mut lines = text.lines(); + /// + /// assert_eq!(Some("foo"), lines.next()); + /// assert_eq!(Some("bar"), lines.next()); + /// assert_eq!(Some(""), lines.next()); + /// assert_eq!(Some("baz"), lines.next()); + /// + /// assert_eq!(None, lines.next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn lines(&self) -> Lines { + StrExt::lines(self) + } + + /// An iterator over the lines of a string. + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.4.0", reason = "use lines() instead now")] + #[inline] + #[allow(deprecated)] + pub fn lines_any(&self) -> LinesAny { + StrExt::lines_any(self) + } + + /// Returns an iterator of `u16` over the string encoded as UTF-16. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let text = "Zażółć gęślą jaźń"; + /// + /// let utf8_len = text.len(); + /// let utf16_len = text.encode_utf16().count(); + /// + /// assert!(utf16_len <= utf8_len); + /// ``` + #[stable(feature = "encode_utf16", since = "1.8.0")] + pub fn encode_utf16(&self) -> EncodeUtf16 { + EncodeUtf16::new(self) + } + + /// Returns `true` if the given pattern matches a sub-slice of + /// this string slice. + /// + /// Returns `false` if it does not. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.contains("nana")); + /// assert!(!bananas.contains("apples")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + StrExt::contains(self, pat) + } + + /// Returns `true` if the given pattern matches a prefix of this + /// string slice. + /// + /// Returns `false` if it does not. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.starts_with("bana")); + /// assert!(!bananas.starts_with("nana")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { + StrExt::starts_with(self, pat) + } + + /// Returns `true` if the given pattern matches a suffix of this + /// string slice. + /// + /// Returns `false` if it does not. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let bananas = "bananas"; + /// + /// assert!(bananas.ends_with("anas")); + /// assert!(!bananas.ends_with("nana")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool + where P::Searcher: ReverseSearcher<'a> + { + StrExt::ends_with(self, pat) + } + + /// Returns the byte index of the first character of this string slice that + /// matches the pattern. + /// + /// Returns [`None`] if the pattern doesn't match. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if + /// a character matches. + /// + /// [`char`]: primitive.char.html + /// [`None`]: option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.find('L'), Some(0)); + /// assert_eq!(s.find('é'), Some(14)); + /// assert_eq!(s.find("Léopard"), Some(13)); + /// ``` + /// + /// More complex patterns using point-free style and closures: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.find(char::is_whitespace), Some(5)); + /// assert_eq!(s.find(char::is_lowercase), Some(1)); + /// assert_eq!(s.find(|c: char| c.is_whitespace() || c.is_lowercase()), Some(1)); + /// assert_eq!(s.find(|c: char| (c < 'o') && (c > 'a')), Some(4)); + /// ``` + /// + /// Not finding the pattern: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// let x: &[_] = &['1', '2']; + /// + /// assert_eq!(s.find(x), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { + StrExt::find(self, pat) + } + + /// Returns the byte index of the last character of this string slice that + /// matches the pattern. + /// + /// Returns [`None`] if the pattern doesn't match. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if + /// a character matches. + /// + /// [`char`]: primitive.char.html + /// [`None`]: option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.rfind('L'), Some(13)); + /// assert_eq!(s.rfind('é'), Some(14)); + /// ``` + /// + /// More complex patterns with closures: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// + /// assert_eq!(s.rfind(char::is_whitespace), Some(12)); + /// assert_eq!(s.rfind(char::is_lowercase), Some(20)); + /// ``` + /// + /// Not finding the pattern: + /// + /// ``` + /// let s = "Löwe 老虎 Léopard"; + /// let x: &[_] = &['1', '2']; + /// + /// assert_eq!(s.rfind(x), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rfind(self, pat) + } + + /// An iterator over substrings of this string slice, separated by + /// characters matched by a pattern. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines the + /// split. + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, eg, [`char`] but not for `&str`. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rsplit`] method can be used. + /// + /// [`char`]: primitive.char.html + /// [`rsplit`]: #method.rsplit + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".split(' ').collect(); + /// assert_eq!(v, ["Mary", "had", "a", "little", "lamb"]); + /// + /// let v: Vec<&str> = "".split('X').collect(); + /// assert_eq!(v, [""]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".split('X').collect(); + /// assert_eq!(v, ["lion", "", "tiger", "leopard"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".split("::").collect(); + /// assert_eq!(v, ["lion", "tiger", "leopard"]); + /// + /// let v: Vec<&str> = "abc1def2ghi".split(char::is_numeric).collect(); + /// assert_eq!(v, ["abc", "def", "ghi"]); + /// + /// let v: Vec<&str> = "lionXtigerXleopard".split(char::is_uppercase).collect(); + /// assert_eq!(v, ["lion", "tiger", "leopard"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".split(|c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["abc", "def", "ghi"]); + /// ``` + /// + /// If a string contains multiple contiguous separators, you will end up + /// with empty strings in the output: + /// + /// ``` + /// let x = "||||a||b|c".to_string(); + /// let d: Vec<_> = x.split('|').collect(); + /// + /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); + /// ``` + /// + /// Contiguous separators are separated by the empty string. + /// + /// ``` + /// let x = "(///)".to_string(); + /// let d: Vec<_> = x.split('/').collect(); + /// + /// assert_eq!(d, &["(", "", "", ")"]); + /// ``` + /// + /// Separators at the start or end of a string are neighbored + /// by empty strings. + /// + /// ``` + /// let d: Vec<_> = "010".split("0").collect(); + /// assert_eq!(d, &["", "1", ""]); + /// ``` + /// + /// When the empty string is used as a separator, it separates + /// every character in the string, along with the beginning + /// and end of the string. + /// + /// ``` + /// let f: Vec<_> = "rust".split("").collect(); + /// assert_eq!(f, &["", "r", "u", "s", "t", ""]); + /// ``` + /// + /// Contiguous separators can lead to possibly surprising behavior + /// when whitespace is used as the separator. This code is correct: + /// + /// ``` + /// let x = " a b c".to_string(); + /// let d: Vec<_> = x.split(' ').collect(); + /// + /// assert_eq!(d, &["", "", "", "", "a", "", "b", "c"]); + /// ``` + /// + /// It does _not_ give you: + /// + /// ```,ignore + /// assert_eq!(d, &["a", "b", "c"]); + /// ``` + /// + /// Use [`split_whitespace`] for this behavior. + /// + /// [`split_whitespace`]: #method.split_whitespace + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { + StrExt::split(self, pat) + } + + /// An iterator over substrings of the given string slice, separated by + /// characters matched by a pattern and yielded in reverse order. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines the + /// split. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// + /// For iterating from the front, the [`split`] method can be used. + /// + /// [`split`]: #method.split + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".rsplit(' ').collect(); + /// assert_eq!(v, ["lamb", "little", "a", "had", "Mary"]); + /// + /// let v: Vec<&str> = "".rsplit('X').collect(); + /// assert_eq!(v, [""]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".rsplit('X').collect(); + /// assert_eq!(v, ["leopard", "tiger", "", "lion"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".rsplit("::").collect(); + /// assert_eq!(v, ["leopard", "tiger", "lion"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".rsplit(|c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["ghi", "def", "abc"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rsplit(self, pat) + } + + /// An iterator over substrings of the given string slice, separated by + /// characters matched by a pattern. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines the + /// split. + /// + /// Equivalent to [`split`], except that the trailing substring + /// is skipped if empty. + /// + /// [`split`]: #method.split + /// + /// This method can be used for string data that is _terminated_, + /// rather than _separated_ by a pattern. + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, eg, [`char`] but not for `&str`. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// [`char`]: primitive.char.html + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rsplit_terminator`] method can be used. + /// + /// [`rsplit_terminator`]: #method.rsplit_terminator + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "A.B.".split_terminator('.').collect(); + /// assert_eq!(v, ["A", "B"]); + /// + /// let v: Vec<&str> = "A..B..".split_terminator(".").collect(); + /// assert_eq!(v, ["A", "", "B", ""]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { + StrExt::split_terminator(self, pat) + } + + /// An iterator over substrings of `self`, separated by characters + /// matched by a pattern and yielded in reverse order. + /// + /// The pattern can be a simple `&str`, [`char`], or a closure that + /// determines the split. + /// Additional libraries might provide more complex patterns like + /// regular expressions. + /// + /// [`char`]: primitive.char.html + /// + /// Equivalent to [`split`], except that the trailing substring is + /// skipped if empty. + /// + /// [`split`]: #method.split + /// + /// This method can be used for string data that is _terminated_, + /// rather than _separated_ by a pattern. + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a + /// reverse search, and it will be double ended if a forward/reverse + /// search yields the same elements. + /// + /// For iterating from the front, the [`split_terminator`] method can be + /// used. + /// + /// [`split_terminator`]: #method.split_terminator + /// + /// # Examples + /// + /// ``` + /// let v: Vec<&str> = "A.B.".rsplit_terminator('.').collect(); + /// assert_eq!(v, ["B", "A"]); + /// + /// let v: Vec<&str> = "A..B..".rsplit_terminator(".").collect(); + /// assert_eq!(v, ["", "B", "", "A"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rsplit_terminator(self, pat) + } + + /// An iterator over substrings of the given string slice, separated by a + /// pattern, restricted to returning at most `n` items. + /// + /// If `n` substrings are returned, the last substring (the `n`th substring) + /// will contain the remainder of the string. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines the + /// split. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator will not be double ended, because it is + /// not efficient to support. + /// + /// If the pattern allows a reverse search, the [`rsplitn`] method can be + /// used. + /// + /// [`rsplitn`]: #method.rsplitn + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lambda".splitn(3, ' ').collect(); + /// assert_eq!(v, ["Mary", "had", "a little lambda"]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".splitn(3, "X").collect(); + /// assert_eq!(v, ["lion", "", "tigerXleopard"]); + /// + /// let v: Vec<&str> = "abcXdef".splitn(1, 'X').collect(); + /// assert_eq!(v, ["abcXdef"]); + /// + /// let v: Vec<&str> = "".splitn(1, 'X').collect(); + /// assert_eq!(v, [""]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".splitn(2, |c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["abc", "defXghi"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { + StrExt::splitn(self, n, pat) + } + + /// An iterator over substrings of this string slice, separated by a + /// pattern, starting from the end of the string, restricted to returning + /// at most `n` items. + /// + /// If `n` substrings are returned, the last substring (the `n`th substring) + /// will contain the remainder of the string. + /// + /// The pattern can be a `&str`, [`char`], or a closure that + /// determines the split. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator will not be double ended, because it is not + /// efficient to support. + /// + /// For splitting from the front, the [`splitn`] method can be used. + /// + /// [`splitn`]: #method.splitn + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// let v: Vec<&str> = "Mary had a little lamb".rsplitn(3, ' ').collect(); + /// assert_eq!(v, ["lamb", "little", "Mary had a"]); + /// + /// let v: Vec<&str> = "lionXXtigerXleopard".rsplitn(3, 'X').collect(); + /// assert_eq!(v, ["leopard", "tiger", "lionX"]); + /// + /// let v: Vec<&str> = "lion::tiger::leopard".rsplitn(2, "::").collect(); + /// assert_eq!(v, ["leopard", "lion::tiger"]); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// let v: Vec<&str> = "abc1defXghi".rsplitn(2, |c| c == '1' || c == 'X').collect(); + /// assert_eq!(v, ["ghi", "abc1def"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rsplitn(self, n, pat) + } + + /// An iterator over the disjoint matches of a pattern within the given string + /// slice. + /// + /// The pattern can be a `&str`, [`char`], or a closure that + /// determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, eg, [`char`] but not for `&str`. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// [`char`]: primitive.char.html + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rmatches`] method can be used. + /// + /// [`rmatches`]: #method.rmatches + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "abcXXXabcYYYabc".matches("abc").collect(); + /// assert_eq!(v, ["abc", "abc", "abc"]); + /// + /// let v: Vec<&str> = "1abc2abc3".matches(char::is_numeric).collect(); + /// assert_eq!(v, ["1", "2", "3"]); + /// ``` + #[stable(feature = "str_matches", since = "1.2.0")] + #[inline] + pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { + StrExt::matches(self, pat) } + /// An iterator over the disjoint matches of a pattern within this string slice, + /// yielded in reverse order. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if + /// a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// + /// For iterating from the front, the [`matches`] method can be used. + /// + /// [`matches`]: #method.matches + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<&str> = "abcXXXabcYYYabc".rmatches("abc").collect(); + /// assert_eq!(v, ["abc", "abc", "abc"]); + /// + /// let v: Vec<&str> = "1abc2abc3".rmatches(char::is_numeric).collect(); + /// assert_eq!(v, ["3", "2", "1"]); + /// ``` + #[stable(feature = "str_matches", since = "1.2.0")] #[inline] - fn is_alphanumeric(&self) -> bool { - self.chars().all(|c| c.is_alphanumeric()) + pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rmatches(self, pat) } + /// An iterator over the disjoint matches of a pattern within this string + /// slice as well as the index that the match starts at. + /// + /// For matches of `pat` within `self` that overlap, only the indices + /// corresponding to the first match are returned. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines + /// if a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator will be a [`DoubleEndedIterator`] if the pattern + /// allows a reverse search and forward/reverse search yields the same + /// elements. This is true for, eg, [`char`] but not for `&str`. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// + /// If the pattern allows a reverse search but its results might differ + /// from a forward search, the [`rmatch_indices`] method can be used. + /// + /// [`rmatch_indices`]: #method.rmatch_indices + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<_> = "abcXXXabcYYYabc".match_indices("abc").collect(); + /// assert_eq!(v, [(0, "abc"), (6, "abc"), (12, "abc")]); + /// + /// let v: Vec<_> = "1abcabc2".match_indices("abc").collect(); + /// assert_eq!(v, [(1, "abc"), (4, "abc")]); + /// + /// let v: Vec<_> = "ababa".match_indices("aba").collect(); + /// assert_eq!(v, [(0, "aba")]); // only the first `aba` + /// ``` + #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) + pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { + StrExt::match_indices(self, pat) } + /// An iterator over the disjoint matches of a pattern within `self`, + /// yielded in reverse order along with the index of the match. + /// + /// For matches of `pat` within `self` that overlap, only the indices + /// corresponding to the last match are returned. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if a + /// character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Iterator behavior + /// + /// The returned iterator requires that the pattern supports a reverse + /// search, and it will be a [`DoubleEndedIterator`] if a forward/reverse + /// search yields the same elements. + /// + /// [`DoubleEndedIterator`]: iter/trait.DoubleEndedIterator.html + /// + /// For iterating from the front, the [`match_indices`] method can be used. + /// + /// [`match_indices`]: #method.match_indices + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let v: Vec<_> = "abcXXXabcYYYabc".rmatch_indices("abc").collect(); + /// assert_eq!(v, [(12, "abc"), (6, "abc"), (0, "abc")]); + /// + /// let v: Vec<_> = "1abcabc2".rmatch_indices("abc").collect(); + /// assert_eq!(v, [(4, "abc"), (1, "abc")]); + /// + /// let v: Vec<_> = "ababa".rmatch_indices("aba").collect(); + /// assert_eq!(v, [(2, "aba")]); // only the last `aba` + /// ``` + #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] - fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) + pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> + where P::Searcher: ReverseSearcher<'a> + { + StrExt::rmatch_indices(self, pat) + } + + /// Returns a string slice with leading and trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!("Hello\tworld", s.trim()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim(&self) -> &str { + StrExt::trim(self) + } + + /// Returns a string slice with leading whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!("Hello\tworld\t", s.trim_left()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = " English"; + /// assert!(Some('E') == s.trim_left().chars().next()); + /// + /// let s = " עברית"; + /// assert!(Some('ע') == s.trim_left().chars().next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim_left(&self) -> &str { + StrExt::trim_left(self) + } + + /// Returns a string slice with trailing whitespace removed. + /// + /// 'Whitespace' is defined according to the terms of the Unicode Derived + /// Core Property `White_Space`. + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// let s = " Hello\tworld\t"; + /// + /// assert_eq!(" Hello\tworld", s.trim_right()); + /// ``` + /// + /// Directionality: + /// + /// ``` + /// let s = "English "; + /// assert!(Some('h') == s.trim_right().chars().rev().next()); + /// + /// let s = "עברית "; + /// assert!(Some('ת') == s.trim_right().chars().rev().next()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim_right(&self) -> &str { + StrExt::trim_right(self) + } + + /// Returns a string slice with all prefixes and suffixes that match a + /// pattern repeatedly removed. + /// + /// The pattern can be a [`char`] or a closure that determines if a + /// character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_matches('1'), "foo1bar"); + /// assert_eq!("123foo1bar123".trim_matches(char::is_numeric), "foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_matches(x), "foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1foo1barXX".trim_matches(|c| c == '1' || c == 'X'), "foo1bar"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str + where P::Searcher: DoubleEndedSearcher<'a> + { + StrExt::trim_matches(self, pat) + } + + /// Returns a string slice with all prefixes that match a pattern + /// repeatedly removed. + /// + /// The pattern can be a `&str`, [`char`], or a closure that determines if + /// a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Left' in this context means the first + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _right_ side, not the left. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_left_matches('1'), "foo1bar11"); + /// assert_eq!("123foo1bar123".trim_left_matches(char::is_numeric), "foo1bar123"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_left_matches(x), "foo1bar12"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { + StrExt::trim_left_matches(self, pat) + } + + /// Returns a string slice with all suffixes that match a pattern + /// repeatedly removed. + /// + /// The pattern can be a `&str`, [`char`], or a closure that + /// determines if a character matches. + /// + /// [`char`]: primitive.char.html + /// + /// # Text directionality + /// + /// A string is a sequence of bytes. 'Right' in this context means the last + /// position of that byte string; for a language like Arabic or Hebrew + /// which are 'right to left' rather than 'left to right', this will be + /// the _left_ side, not the right. + /// + /// # Examples + /// + /// Simple patterns: + /// + /// ``` + /// assert_eq!("11foo1bar11".trim_right_matches('1'), "11foo1bar"); + /// assert_eq!("123foo1bar123".trim_right_matches(char::is_numeric), "123foo1bar"); + /// + /// let x: &[_] = &['1', '2']; + /// assert_eq!("12foo1bar12".trim_right_matches(x), "12foo1bar"); + /// ``` + /// + /// A more complex pattern, using a closure: + /// + /// ``` + /// assert_eq!("1fooX".trim_right_matches(|c| c == '1' || c == 'X'), "1foo"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str + where P::Searcher: ReverseSearcher<'a> + { + StrExt::trim_right_matches(self, pat) + } + + /// Parses this string slice into another type. + /// + /// Because `parse` is so general, it can cause problems with type + /// inference. As such, `parse` is one of the few times you'll see + /// the syntax affectionately known as the 'turbofish': `::<>`. This + /// helps the inference algorithm understand specifically which type + /// you're trying to parse into. + /// + /// `parse` can parse any type that implements the [`FromStr`] trait. + /// + /// [`FromStr`]: str/trait.FromStr.html + /// + /// # Errors + /// + /// Will return [`Err`] if it's not possible to parse this string slice into + /// the desired type. + /// + /// [`Err`]: str/trait.FromStr.html#associatedtype.Err + /// + /// # Examples + /// + /// Basic usage + /// + /// ``` + /// let four: u32 = "4".parse().unwrap(); + /// + /// assert_eq!(4, four); + /// ``` + /// + /// Using the 'turbofish' instead of annotating `four`: + /// + /// ``` + /// let four = "4".parse::(); + /// + /// assert_eq!(Ok(4), four); + /// ``` + /// + /// Failing to parse: + /// + /// ``` + /// let nope = "j".parse::(); + /// + /// assert!(nope.is_err()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn parse(&self) -> Result { + StrExt::parse(self) } + /// Checks if all characters in this string are within the ASCII range. + /// + /// # Examples + /// + /// ``` + /// let ascii = "hello!\n"; + /// let non_ascii = "Grüße, Jürgen ❤"; + /// + /// assert!(ascii.is_ascii()); + /// assert!(!non_ascii.is_ascii()); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] - fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) + pub fn is_ascii(&self) -> bool { + // We can treat each byte as character here: all multibyte characters + // start with a byte that is not in the ascii range, so we will stop + // there already. + self.bytes().all(|b| b.is_ascii()) + } + + /// Checks that two strings are an ASCII case-insensitive match. + /// + /// Same as `to_ascii_lowercase(a) == to_ascii_lowercase(b)`, + /// but without allocating and copying temporaries. + /// + /// # Examples + /// + /// ``` + /// assert!("Ferris".eq_ignore_ascii_case("FERRIS")); + /// assert!("Ferrös".eq_ignore_ascii_case("FERRöS")); + /// assert!(!"Ferrös".eq_ignore_ascii_case("FERRÖS")); + /// ``` + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + #[inline] + pub fn eq_ignore_ascii_case(&self, other: &str) -> bool { + self.as_bytes().eq_ignore_ascii_case(other.as_bytes()) } + + /// Converts this string to its ASCII upper case equivalent in-place. + /// + /// ASCII letters 'a' to 'z' are mapped to 'A' to 'Z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new uppercased value without modifying the existing one, use + /// [`to_ascii_uppercase`]. + /// + /// [`to_ascii_uppercase`]: #method.to_ascii_uppercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + pub fn make_ascii_uppercase(&mut self) { + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_uppercase() + } + + /// Converts this string to its ASCII lower case equivalent in-place. + /// + /// ASCII letters 'A' to 'Z' are mapped to 'a' to 'z', + /// but non-ASCII letters are unchanged. + /// + /// To return a new lowercased value without modifying the existing one, use + /// [`to_ascii_lowercase`]. + /// + /// [`to_ascii_lowercase`]: #method.to_ascii_lowercase + #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] + pub fn make_ascii_lowercase(&mut self) { + let me = unsafe { self.as_bytes_mut() }; + me.make_ascii_lowercase() + } +}} + +#[lang = "str"] +#[cfg(not(test))] +#[cfg(not(stage0))] +impl str { + str_core_methods!(); } + #[stable(feature = "rust1", since = "1.0.0")] impl AsRef<[u8]> for str { #[inline] @@ -2665,3 +4330,72 @@ impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { #[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for SplitWhitespace<'a> {} + +/// An iterator of [`u16`] over the string encoded as UTF-16. +/// +/// [`u16`]: ../../std/primitive.u16.html +/// +/// This struct is created by the [`encode_utf16`] method on [`str`]. +/// See its documentation for more. +/// +/// [`encode_utf16`]: ../../std/primitive.str.html#method.encode_utf16 +/// [`str`]: ../../std/primitive.str.html +#[derive(Clone)] +#[stable(feature = "encode_utf16", since = "1.8.0")] +pub struct EncodeUtf16<'a> { + chars: Chars<'a>, + extra: u16, +} + +// FIXME: remove (inline) this method +// when updating to a bootstrap compiler that has the new lang items. +// For grepping purpose: #[cfg(stage0)] +impl<'a> EncodeUtf16<'a> { + #[unstable(feature = "core_str_ext", issue = "32110")] + #[doc(hidden)] + pub fn new(s: &'a str) -> Self { + EncodeUtf16 { chars: s.chars(), extra: 0 } + } +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a> fmt::Debug for EncodeUtf16<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.pad("EncodeUtf16 { .. }") + } +} + +#[stable(feature = "encode_utf16", since = "1.8.0")] +impl<'a> Iterator for EncodeUtf16<'a> { + type Item = u16; + + #[inline] + fn next(&mut self) -> Option { + if self.extra != 0 { + let tmp = self.extra; + self.extra = 0; + return Some(tmp); + } + + let mut buf = [0; 2]; + self.chars.next().map(|ch| { + let n = ch.encode_utf16(&mut buf).len(); + if n == 2 { + self.extra = buf[1]; + } + buf[0] + }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let (low, high) = self.chars.size_hint(); + // every char gets either one u16 or two u16, + // so this iterator is between 1 or 2 times as + // long as the underlying iterator. + (low, high.and_then(|n| n.checked_mul(2))) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a> FusedIterator for EncodeUtf16<'a> {} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 237a22925b4..24c7f3b0aba 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -214,6 +214,7 @@ language_item_table! { StrImplItem, "str", str_impl; SliceImplItem, "slice", slice_impl; SliceU8ImplItem, "slice_u8", slice_u8_impl; + StrAllocImplItem, "str_alloc", str_alloc_impl; SliceAllocImplItem, "slice_alloc", slice_alloc_impl; SliceU8AllocImplItem, "slice_u8_alloc", slice_u8_alloc_impl; ConstPtrImplItem, "const_ptr", const_ptr_impl; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index ea4c2c08817..c538004c838 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -471,6 +471,9 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { ty::TyStr => { let lang_def_id = lang_items.str_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + + let lang_def_id = lang_items.str_alloc_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::TySlice(_) => { let lang_def_id = lang_items.slice_impl(); diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 91f9e3e6fba..97e57ba668f 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -122,7 +122,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyStr => { self.check_primitive_impl(def_id, lang_items.str_impl(), - None, + lang_items.str_alloc_impl(), "str", "str", item.span); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 38af4350815..65f6b227a56 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -290,6 +290,7 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec Date: Sun, 8 Apr 2018 10:09:52 +0200 Subject: Add some f32 and f64 inherent methods in libcore MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … previously in the unstable core::num::Float trait. Per https://github.com/rust-lang/rust/issues/32110#issuecomment-379503183, the `abs`, `signum`, and `powi` methods are *not* included for now since they rely on LLVM intrinsics and we haven’t determined yet whether those instrinsics lower to calls to libm functions on any platform. --- src/liballoc/vec.rs | 1 + src/libcore/lib.rs | 1 + src/libcore/num/f32.rs | 284 +++++++++++++++++++++++ src/libcore/num/f64.rs | 296 +++++++++++++++++++++++- src/librustc/middle/lang_items.rs | 2 + src/librustc_typeck/check/method/probe.rs | 6 + src/librustc_typeck/coherence/inherent_impls.rs | 4 +- src/librustdoc/clean/inline.rs | 2 + src/libstd/f32.rs | 283 +--------------------- src/libstd/f64.rs | 291 +---------------------- 10 files changed, 611 insertions(+), 559 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 7d1b2ed85c7..b184404c15b 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -74,6 +74,7 @@ use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem; #[cfg(not(test))] +#[cfg(stage0)] use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds}; diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5a107951b0b..215886069f5 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -71,6 +71,7 @@ #![feature(cfg_target_has_atomic)] #![feature(concat_idents)] #![feature(const_fn)] +#![feature(core_float)] #![feature(custom_attribute)] #![feature(doc_cfg)] #![feature(doc_spotlight)] diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 3586fa5442f..0edf63bce12 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -20,6 +20,7 @@ use intrinsics; use mem; use num::Float; +#[cfg(not(stage0))] use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f32`. @@ -292,3 +293,286 @@ impl Float for f32 { unsafe { mem::transmute(v) } } } + +// FIXME: remove (inline) this macro and the Float trait +// when updating to a bootstrap compiler that has the new lang items. +#[cfg_attr(stage0, macro_export)] +#[unstable(feature = "core_float", issue = "32110")] +macro_rules! f32_core_methods { () => { + /// Returns `true` if this value is `NaN` and false otherwise. + /// + /// ``` + /// use std::f32; + /// + /// let nan = f32::NAN; + /// let f = 7.0_f32; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_nan(self) -> bool { Float::is_nan(self) } + + /// Returns `true` if this value is positive infinity or negative infinity and + /// false otherwise. + /// + /// ``` + /// use std::f32; + /// + /// let f = 7.0f32; + /// let inf = f32::INFINITY; + /// let neg_inf = f32::NEG_INFINITY; + /// let nan = f32::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_infinite(self) -> bool { Float::is_infinite(self) } + + /// Returns `true` if this number is neither infinite nor `NaN`. + /// + /// ``` + /// use std::f32; + /// + /// let f = 7.0f32; + /// let inf = f32::INFINITY; + /// let neg_inf = f32::NEG_INFINITY; + /// let nan = f32::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_finite(self) -> bool { Float::is_finite(self) } + + /// Returns `true` if the number is neither zero, infinite, + /// [subnormal][subnormal], or `NaN`. + /// + /// ``` + /// use std::f32; + /// + /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 + /// let max = f32::MAX; + /// let lower_than_min = 1.0e-40_f32; + /// let zero = 0.0_f32; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f32::NAN.is_normal()); + /// assert!(!f32::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_normal(self) -> bool { Float::is_normal(self) } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// use std::num::FpCategory; + /// use std::f32; + /// + /// let num = 12.4_f32; + /// let inf = f32::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn classify(self) -> FpCategory { Float::classify(self) } + + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. + /// + /// ``` + /// let f = 7.0_f32; + /// let g = -7.0_f32; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_sign_positive(self) -> bool { Float::is_sign_positive(self) } + + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. + /// + /// ``` + /// let f = 7.0f32; + /// let g = -7.0f32; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_sign_negative(self) -> bool { Float::is_sign_negative(self) } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// use std::f32; + /// + /// let x = 2.0_f32; + /// let abs_difference = (x.recip() - (1.0/x)).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn recip(self) -> f32 { Float::recip(self) } + + /// Converts radians to degrees. + /// + /// ``` + /// use std::f32::{self, consts}; + /// + /// let angle = consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] + #[inline] + pub fn to_degrees(self) -> f32 { Float::to_degrees(self) } + + /// Converts degrees to radians. + /// + /// ``` + /// use std::f32::{self, consts}; + /// + /// let angle = 180.0f32; + /// + /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// + /// assert!(abs_difference <= f32::EPSILON); + /// ``` + #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] + #[inline] + pub fn to_radians(self) -> f32 { Float::to_radians(self) } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// let x = 1.0f32; + /// let y = 2.0f32; + /// + /// assert_eq!(x.max(y), y); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn max(self, other: f32) -> f32 { + Float::max(self, other) + } + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// let x = 1.0f32; + /// let y = 2.0f32; + /// + /// assert_eq!(x.min(y), x); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn min(self, other: f32) -> f32 { + Float::min(self, other) + } + + /// Raw transmutation to `u32`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// assert_ne!((1f32).to_bits(), 1f32 as u32); // to_bits() is not casting! + /// assert_eq!((12.5f32).to_bits(), 0x41480000); + /// + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[inline] + pub fn to_bits(self) -> u32 { + Float::to_bits(self) + } + + /// Raw transmutation from `u32`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favours preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// use std::f32; + /// let v = f32::from_bits(0x41480000); + /// let difference = (v - 12.5).abs(); + /// assert!(difference <= 1e-5); + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[inline] + pub fn from_bits(v: u32) -> Self { + Float::from_bits(v) + } +}} + +#[lang = "f32"] +#[cfg(not(test))] +#[cfg(not(stage0))] +impl f32 { + f32_core_methods!(); +} diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 64c0d508b38..38f3d63ea8d 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -19,8 +19,9 @@ use intrinsics; use mem; -use num::FpCategory as Fp; use num::Float; +#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory as Fp; /// The radix or base of the internal representation of `f64`. #[stable(feature = "rust1", since = "1.0.0")] @@ -291,3 +292,296 @@ impl Float for f64 { unsafe { mem::transmute(v) } } } + +// FIXME: remove (inline) this macro and the Float trait +// when updating to a bootstrap compiler that has the new lang items. +#[cfg_attr(stage0, macro_export)] +#[unstable(feature = "core_float", issue = "32110")] +macro_rules! f64_core_methods { () => { + /// Returns `true` if this value is `NaN` and false otherwise. + /// + /// ``` + /// use std::f64; + /// + /// let nan = f64::NAN; + /// let f = 7.0_f64; + /// + /// assert!(nan.is_nan()); + /// assert!(!f.is_nan()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_nan(self) -> bool { Float::is_nan(self) } + + /// Returns `true` if this value is positive infinity or negative infinity and + /// false otherwise. + /// + /// ``` + /// use std::f64; + /// + /// let f = 7.0f64; + /// let inf = f64::INFINITY; + /// let neg_inf = f64::NEG_INFINITY; + /// let nan = f64::NAN; + /// + /// assert!(!f.is_infinite()); + /// assert!(!nan.is_infinite()); + /// + /// assert!(inf.is_infinite()); + /// assert!(neg_inf.is_infinite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_infinite(self) -> bool { Float::is_infinite(self) } + + /// Returns `true` if this number is neither infinite nor `NaN`. + /// + /// ``` + /// use std::f64; + /// + /// let f = 7.0f64; + /// let inf: f64 = f64::INFINITY; + /// let neg_inf: f64 = f64::NEG_INFINITY; + /// let nan: f64 = f64::NAN; + /// + /// assert!(f.is_finite()); + /// + /// assert!(!nan.is_finite()); + /// assert!(!inf.is_finite()); + /// assert!(!neg_inf.is_finite()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_finite(self) -> bool { Float::is_finite(self) } + + /// Returns `true` if the number is neither zero, infinite, + /// [subnormal][subnormal], or `NaN`. + /// + /// ``` + /// use std::f64; + /// + /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64 + /// let max = f64::MAX; + /// let lower_than_min = 1.0e-308_f64; + /// let zero = 0.0f64; + /// + /// assert!(min.is_normal()); + /// assert!(max.is_normal()); + /// + /// assert!(!zero.is_normal()); + /// assert!(!f64::NAN.is_normal()); + /// assert!(!f64::INFINITY.is_normal()); + /// // Values between `0` and `min` are Subnormal. + /// assert!(!lower_than_min.is_normal()); + /// ``` + /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_normal(self) -> bool { Float::is_normal(self) } + + /// Returns the floating point category of the number. If only one property + /// is going to be tested, it is generally faster to use the specific + /// predicate instead. + /// + /// ``` + /// use std::num::FpCategory; + /// use std::f64; + /// + /// let num = 12.4_f64; + /// let inf = f64::INFINITY; + /// + /// assert_eq!(num.classify(), FpCategory::Normal); + /// assert_eq!(inf.classify(), FpCategory::Infinite); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn classify(self) -> FpCategory { Float::classify(self) } + + /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with + /// positive sign bit and positive infinity. + /// + /// ``` + /// let f = 7.0_f64; + /// let g = -7.0_f64; + /// + /// assert!(f.is_sign_positive()); + /// assert!(!g.is_sign_positive()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_sign_positive(self) -> bool { Float::is_sign_positive(self) } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_positive")] + #[inline] + #[doc(hidden)] + pub fn is_positive(self) -> bool { Float::is_sign_positive(self) } + + /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with + /// negative sign bit and negative infinity. + /// + /// ``` + /// let f = 7.0_f64; + /// let g = -7.0_f64; + /// + /// assert!(!f.is_sign_negative()); + /// assert!(g.is_sign_negative()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn is_sign_negative(self) -> bool { Float::is_sign_negative(self) } + + #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_negative")] + #[inline] + #[doc(hidden)] + pub fn is_negative(self) -> bool { Float::is_sign_negative(self) } + + /// Takes the reciprocal (inverse) of a number, `1/x`. + /// + /// ``` + /// let x = 2.0_f64; + /// let abs_difference = (x.recip() - (1.0/x)).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn recip(self) -> f64 { Float::recip(self) } + + /// Converts radians to degrees. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = consts::PI; + /// + /// let abs_difference = (angle.to_degrees() - 180.0).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn to_degrees(self) -> f64 { Float::to_degrees(self) } + + /// Converts degrees to radians. + /// + /// ``` + /// use std::f64::consts; + /// + /// let angle = 180.0_f64; + /// + /// let abs_difference = (angle.to_radians() - consts::PI).abs(); + /// + /// assert!(abs_difference < 1e-10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn to_radians(self) -> f64 { Float::to_radians(self) } + + /// Returns the maximum of the two numbers. + /// + /// ``` + /// let x = 1.0_f64; + /// let y = 2.0_f64; + /// + /// assert_eq!(x.max(y), y); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn max(self, other: f64) -> f64 { + Float::max(self, other) + } + + /// Returns the minimum of the two numbers. + /// + /// ``` + /// let x = 1.0_f64; + /// let y = 2.0_f64; + /// + /// assert_eq!(x.min(y), x); + /// ``` + /// + /// If one of the arguments is NaN, then the other argument is returned. + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn min(self, other: f64) -> f64 { + Float::min(self, other) + } + + /// Raw transmutation to `u64`. + /// + /// This is currently identical to `transmute::(self)` on all platforms. + /// + /// See `from_bits` for some discussion of the portability of this operation + /// (there are almost no issues). + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// assert!((1f64).to_bits() != 1f64 as u64); // to_bits() is not casting! + /// assert_eq!((12.5f64).to_bits(), 0x4029000000000000); + /// + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[inline] + pub fn to_bits(self) -> u64 { + Float::to_bits(self) + } + + /// Raw transmutation from `u64`. + /// + /// This is currently identical to `transmute::(v)` on all platforms. + /// It turns out this is incredibly portable, for two reasons: + /// + /// * Floats and Ints have the same endianness on all supported platforms. + /// * IEEE-754 very precisely specifies the bit layout of floats. + /// + /// However there is one caveat: prior to the 2008 version of IEEE-754, how + /// to interpret the NaN signaling bit wasn't actually specified. Most platforms + /// (notably x86 and ARM) picked the interpretation that was ultimately + /// standardized in 2008, but some didn't (notably MIPS). As a result, all + /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. + /// + /// Rather than trying to preserve signaling-ness cross-platform, this + /// implementation favours preserving the exact bits. This means that + /// any payloads encoded in NaNs will be preserved even if the result of + /// this method is sent over the network from an x86 machine to a MIPS one. + /// + /// If the results of this method are only manipulated by the same + /// architecture that produced them, then there is no portability concern. + /// + /// If the input isn't NaN, then there is no portability concern. + /// + /// If you don't care about signalingness (very likely), then there is no + /// portability concern. + /// + /// Note that this function is distinct from `as` casting, which attempts to + /// preserve the *numeric* value, and not the bitwise value. + /// + /// # Examples + /// + /// ``` + /// use std::f64; + /// let v = f64::from_bits(0x4029000000000000); + /// let difference = (v - 12.5).abs(); + /// assert!(difference <= 1e-5); + /// ``` + #[stable(feature = "float_bits_conv", since = "1.20.0")] + #[inline] + pub fn from_bits(v: u64) -> Self { + Float::from_bits(v) + } +}} + +#[lang = "f64"] +#[cfg(not(test))] +#[cfg(not(stage0))] +impl f64 { + f64_core_methods!(); +} diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index 24c7f3b0aba..c7412dbeeb3 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -233,6 +233,8 @@ language_item_table! { UsizeImplItem, "usize", usize_impl; F32ImplItem, "f32", f32_impl; F64ImplItem, "f64", f64_impl; + F32RuntimeImplItem, "f32_runtime", f32_runtime_impl; + F64RuntimeImplItem, "f64_runtime", f64_runtime_impl; SizedTraitLangItem, "sized", sized_trait; UnsizeTraitLangItem, "unsize", unsize_trait; diff --git a/src/librustc_typeck/check/method/probe.rs b/src/librustc_typeck/check/method/probe.rs index c538004c838..073f36b9f3c 100644 --- a/src/librustc_typeck/check/method/probe.rs +++ b/src/librustc_typeck/check/method/probe.rs @@ -547,10 +547,16 @@ impl<'a, 'gcx, 'tcx> ProbeContext<'a, 'gcx, 'tcx> { ty::TyFloat(ast::FloatTy::F32) => { let lang_def_id = lang_items.f32_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + + let lang_def_id = lang_items.f32_runtime_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); } ty::TyFloat(ast::FloatTy::F64) => { let lang_def_id = lang_items.f64_impl(); self.assemble_inherent_impl_for_primitive(lang_def_id); + + let lang_def_id = lang_items.f64_runtime_impl(); + self.assemble_inherent_impl_for_primitive(lang_def_id); } _ => {} } diff --git a/src/librustc_typeck/coherence/inherent_impls.rs b/src/librustc_typeck/coherence/inherent_impls.rs index 97e57ba668f..532f1da4f30 100644 --- a/src/librustc_typeck/coherence/inherent_impls.rs +++ b/src/librustc_typeck/coherence/inherent_impls.rs @@ -258,7 +258,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyFloat(ast::FloatTy::F32) => { self.check_primitive_impl(def_id, lang_items.f32_impl(), - None, + lang_items.f32_runtime_impl(), "f32", "f32", item.span); @@ -266,7 +266,7 @@ impl<'a, 'tcx, 'v> ItemLikeVisitor<'v> for InherentCollect<'a, 'tcx> { ty::TyFloat(ast::FloatTy::F64) => { self.check_primitive_impl(def_id, lang_items.f64_impl(), - None, + lang_items.f64_runtime_impl(), "f64", "f64", item.span); diff --git a/src/librustdoc/clean/inline.rs b/src/librustdoc/clean/inline.rs index 65f6b227a56..23e0c2625ee 100644 --- a/src/librustdoc/clean/inline.rs +++ b/src/librustdoc/clean/inline.rs @@ -286,6 +286,8 @@ pub fn build_impls(cx: &DocContext, did: DefId, auto_traits: bool) -> Vec bool { num::Float::is_nan(self) } - - /// Returns `true` if this value is positive infinity or negative infinity and - /// false otherwise. - /// - /// ``` - /// use std::f32; - /// - /// let f = 7.0f32; - /// let inf = f32::INFINITY; - /// let neg_inf = f32::NEG_INFINITY; - /// let nan = f32::NAN; - /// - /// assert!(!f.is_infinite()); - /// assert!(!nan.is_infinite()); - /// - /// assert!(inf.is_infinite()); - /// assert!(neg_inf.is_infinite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_infinite(self) -> bool { num::Float::is_infinite(self) } - - /// Returns `true` if this number is neither infinite nor `NaN`. - /// - /// ``` - /// use std::f32; - /// - /// let f = 7.0f32; - /// let inf = f32::INFINITY; - /// let neg_inf = f32::NEG_INFINITY; - /// let nan = f32::NAN; - /// - /// assert!(f.is_finite()); - /// - /// assert!(!nan.is_finite()); - /// assert!(!inf.is_finite()); - /// assert!(!neg_inf.is_finite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_finite(self) -> bool { num::Float::is_finite(self) } - - /// Returns `true` if the number is neither zero, infinite, - /// [subnormal][subnormal], or `NaN`. - /// - /// ``` - /// use std::f32; - /// - /// let min = f32::MIN_POSITIVE; // 1.17549435e-38f32 - /// let max = f32::MAX; - /// let lower_than_min = 1.0e-40_f32; - /// let zero = 0.0_f32; - /// - /// assert!(min.is_normal()); - /// assert!(max.is_normal()); - /// - /// assert!(!zero.is_normal()); - /// assert!(!f32::NAN.is_normal()); - /// assert!(!f32::INFINITY.is_normal()); - /// // Values between `0` and `min` are Subnormal. - /// assert!(!lower_than_min.is_normal()); - /// ``` - /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_normal(self) -> bool { num::Float::is_normal(self) } - - /// Returns the floating point category of the number. If only one property - /// is going to be tested, it is generally faster to use the specific - /// predicate instead. - /// - /// ``` - /// use std::num::FpCategory; - /// use std::f32; - /// - /// let num = 12.4_f32; - /// let inf = f32::INFINITY; - /// - /// assert_eq!(num.classify(), FpCategory::Normal); - /// assert_eq!(inf.classify(), FpCategory::Infinite); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn classify(self) -> FpCategory { num::Float::classify(self) } + #[cfg(stage0)] + f32_core_methods!(); /// Returns the largest integer less than or equal to a number. /// @@ -257,7 +163,7 @@ impl f32 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn abs(self) -> f32 { num::Float::abs(self) } + pub fn abs(self) -> f32 { Float::abs(self) } /// Returns a number that represents the sign of `self`. /// @@ -277,35 +183,7 @@ impl f32 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn signum(self) -> f32 { num::Float::signum(self) } - - /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with - /// positive sign bit and positive infinity. - /// - /// ``` - /// let f = 7.0_f32; - /// let g = -7.0_f32; - /// - /// assert!(f.is_sign_positive()); - /// assert!(!g.is_sign_positive()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_positive(self) -> bool { num::Float::is_sign_positive(self) } - - /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with - /// negative sign bit and negative infinity. - /// - /// ``` - /// let f = 7.0f32; - /// let g = -7.0f32; - /// - /// assert!(!f.is_sign_negative()); - /// assert!(g.is_sign_negative()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_negative(self) -> bool { num::Float::is_sign_negative(self) } + pub fn signum(self) -> f32 { Float::signum(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error. This produces a more accurate result with better performance than @@ -380,20 +258,6 @@ impl f32 { } - /// Takes the reciprocal (inverse) of a number, `1/x`. - /// - /// ``` - /// use std::f32; - /// - /// let x = 2.0_f32; - /// let abs_difference = (x.recip() - (1.0/x)).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn recip(self) -> f32 { num::Float::recip(self) } - /// Raises a number to an integer power. /// /// Using this function is generally faster than using `powf` @@ -408,7 +272,7 @@ impl f32 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn powi(self, n: i32) -> f32 { num::Float::powi(self, n) } + pub fn powi(self, n: i32) -> f32 { Float::powi(self, n) } /// Raises a number to a floating point power. /// @@ -584,68 +448,6 @@ impl f32 { return unsafe { intrinsics::log10f32(self) }; } - /// Converts radians to degrees. - /// - /// ``` - /// use std::f32::{self, consts}; - /// - /// let angle = consts::PI; - /// - /// let abs_difference = (angle.to_degrees() - 180.0).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] - #[inline] - pub fn to_degrees(self) -> f32 { num::Float::to_degrees(self) } - - /// Converts degrees to radians. - /// - /// ``` - /// use std::f32::{self, consts}; - /// - /// let angle = 180.0f32; - /// - /// let abs_difference = (angle.to_radians() - consts::PI).abs(); - /// - /// assert!(abs_difference <= f32::EPSILON); - /// ``` - #[stable(feature = "f32_deg_rad_conversions", since="1.7.0")] - #[inline] - pub fn to_radians(self) -> f32 { num::Float::to_radians(self) } - - /// Returns the maximum of the two numbers. - /// - /// ``` - /// let x = 1.0f32; - /// let y = 2.0f32; - /// - /// assert_eq!(x.max(y), y); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn max(self, other: f32) -> f32 { - num::Float::max(self, other) - } - - /// Returns the minimum of the two numbers. - /// - /// ``` - /// let x = 1.0f32; - /// let y = 2.0f32; - /// - /// assert_eq!(x.min(y), x); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn min(self, other: f32) -> f32 { - num::Float::min(self, other) - } - /// The positive difference of two numbers. /// /// * If `self <= other`: `0:0` @@ -1046,73 +848,6 @@ impl f32 { pub fn atanh(self) -> f32 { 0.5 * ((2.0 * self) / (1.0 - self)).ln_1p() } - - /// Raw transmutation to `u32`. - /// - /// This is currently identical to `transmute::(self)` on all platforms. - /// - /// See `from_bits` for some discussion of the portability of this operation - /// (there are almost no issues). - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// assert_ne!((1f32).to_bits(), 1f32 as u32); // to_bits() is not casting! - /// assert_eq!((12.5f32).to_bits(), 0x41480000); - /// - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn to_bits(self) -> u32 { - num::Float::to_bits(self) - } - - /// Raw transmutation from `u32`. - /// - /// This is currently identical to `transmute::(v)` on all platforms. - /// It turns out this is incredibly portable, for two reasons: - /// - /// * Floats and Ints have the same endianness on all supported platforms. - /// * IEEE-754 very precisely specifies the bit layout of floats. - /// - /// However there is one caveat: prior to the 2008 version of IEEE-754, how - /// to interpret the NaN signaling bit wasn't actually specified. Most platforms - /// (notably x86 and ARM) picked the interpretation that was ultimately - /// standardized in 2008, but some didn't (notably MIPS). As a result, all - /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. - /// - /// Rather than trying to preserve signaling-ness cross-platform, this - /// implementation favours preserving the exact bits. This means that - /// any payloads encoded in NaNs will be preserved even if the result of - /// this method is sent over the network from an x86 machine to a MIPS one. - /// - /// If the results of this method are only manipulated by the same - /// architecture that produced them, then there is no portability concern. - /// - /// If the input isn't NaN, then there is no portability concern. - /// - /// If you don't care about signalingness (very likely), then there is no - /// portability concern. - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// use std::f32; - /// let v = f32::from_bits(0x41480000); - /// let difference = (v - 12.5).abs(); - /// assert!(difference <= 1e-5); - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn from_bits(v: u32) -> Self { - num::Float::from_bits(v) - } } #[cfg(test)] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a9585670ad0..d4a8f700a90 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -19,10 +19,11 @@ #![allow(missing_docs)] #[cfg(not(test))] -use core::num; +use core::num::Float; #[cfg(not(test))] use intrinsics; #[cfg(not(test))] +#[cfg(stage0)] use num::FpCategory; #[cfg(not(test))] use sys::cmath; @@ -39,106 +40,11 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; pub use core::f64::consts; #[cfg(not(test))] -#[lang = "f64"] +#[cfg_attr(stage0, lang = "f64")] +#[cfg_attr(not(stage0), lang = "f64_runtime")] impl f64 { - /// Returns `true` if this value is `NaN` and false otherwise. - /// - /// ``` - /// use std::f64; - /// - /// let nan = f64::NAN; - /// let f = 7.0_f64; - /// - /// assert!(nan.is_nan()); - /// assert!(!f.is_nan()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_nan(self) -> bool { num::Float::is_nan(self) } - - /// Returns `true` if this value is positive infinity or negative infinity and - /// false otherwise. - /// - /// ``` - /// use std::f64; - /// - /// let f = 7.0f64; - /// let inf = f64::INFINITY; - /// let neg_inf = f64::NEG_INFINITY; - /// let nan = f64::NAN; - /// - /// assert!(!f.is_infinite()); - /// assert!(!nan.is_infinite()); - /// - /// assert!(inf.is_infinite()); - /// assert!(neg_inf.is_infinite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_infinite(self) -> bool { num::Float::is_infinite(self) } - - /// Returns `true` if this number is neither infinite nor `NaN`. - /// - /// ``` - /// use std::f64; - /// - /// let f = 7.0f64; - /// let inf: f64 = f64::INFINITY; - /// let neg_inf: f64 = f64::NEG_INFINITY; - /// let nan: f64 = f64::NAN; - /// - /// assert!(f.is_finite()); - /// - /// assert!(!nan.is_finite()); - /// assert!(!inf.is_finite()); - /// assert!(!neg_inf.is_finite()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_finite(self) -> bool { num::Float::is_finite(self) } - - /// Returns `true` if the number is neither zero, infinite, - /// [subnormal][subnormal], or `NaN`. - /// - /// ``` - /// use std::f64; - /// - /// let min = f64::MIN_POSITIVE; // 2.2250738585072014e-308f64 - /// let max = f64::MAX; - /// let lower_than_min = 1.0e-308_f64; - /// let zero = 0.0f64; - /// - /// assert!(min.is_normal()); - /// assert!(max.is_normal()); - /// - /// assert!(!zero.is_normal()); - /// assert!(!f64::NAN.is_normal()); - /// assert!(!f64::INFINITY.is_normal()); - /// // Values between `0` and `min` are Subnormal. - /// assert!(!lower_than_min.is_normal()); - /// ``` - /// [subnormal]: https://en.wikipedia.org/wiki/Denormal_number - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_normal(self) -> bool { num::Float::is_normal(self) } - - /// Returns the floating point category of the number. If only one property - /// is going to be tested, it is generally faster to use the specific - /// predicate instead. - /// - /// ``` - /// use std::num::FpCategory; - /// use std::f64; - /// - /// let num = 12.4_f64; - /// let inf = f64::INFINITY; - /// - /// assert_eq!(num.classify(), FpCategory::Normal); - /// assert_eq!(inf.classify(), FpCategory::Infinite); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn classify(self) -> FpCategory { num::Float::classify(self) } + #[cfg(stage0)] + f64_core_methods!(); /// Returns the largest integer less than or equal to a number. /// @@ -235,7 +141,7 @@ impl f64 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn abs(self) -> f64 { num::Float::abs(self) } + pub fn abs(self) -> f64 { Float::abs(self) } /// Returns a number that represents the sign of `self`. /// @@ -255,45 +161,7 @@ impl f64 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn signum(self) -> f64 { num::Float::signum(self) } - - /// Returns `true` if and only if `self` has a positive sign, including `+0.0`, `NaN`s with - /// positive sign bit and positive infinity. - /// - /// ``` - /// let f = 7.0_f64; - /// let g = -7.0_f64; - /// - /// assert!(f.is_sign_positive()); - /// assert!(!g.is_sign_positive()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_positive(self) -> bool { num::Float::is_sign_positive(self) } - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_positive")] - #[inline] - pub fn is_positive(self) -> bool { num::Float::is_sign_positive(self) } - - /// Returns `true` if and only if `self` has a negative sign, including `-0.0`, `NaN`s with - /// negative sign bit and negative infinity. - /// - /// ``` - /// let f = 7.0_f64; - /// let g = -7.0_f64; - /// - /// assert!(!f.is_sign_negative()); - /// assert!(g.is_sign_negative()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn is_sign_negative(self) -> bool { num::Float::is_sign_negative(self) } - - #[stable(feature = "rust1", since = "1.0.0")] - #[rustc_deprecated(since = "1.0.0", reason = "renamed to is_sign_negative")] - #[inline] - pub fn is_negative(self) -> bool { num::Float::is_sign_negative(self) } + pub fn signum(self) -> f64 { Float::signum(self) } /// Fused multiply-add. Computes `(self * a) + b` with only one rounding /// error. This produces a more accurate result with better performance than @@ -365,18 +233,6 @@ impl f64 { } } - /// Takes the reciprocal (inverse) of a number, `1/x`. - /// - /// ``` - /// let x = 2.0_f64; - /// let abs_difference = (x.recip() - (1.0/x)).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn recip(self) -> f64 { num::Float::recip(self) } - /// Raises a number to an integer power. /// /// Using this function is generally faster than using `powf` @@ -389,7 +245,7 @@ impl f64 { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn powi(self, n: i32) -> f64 { num::Float::powi(self, n) } + pub fn powi(self, n: i32) -> f64 { Float::powi(self, n) } /// Raises a number to a floating point power. /// @@ -535,68 +391,6 @@ impl f64 { self.log_wrapper(|n| { unsafe { intrinsics::log10f64(n) } }) } - /// Converts radians to degrees. - /// - /// ``` - /// use std::f64::consts; - /// - /// let angle = consts::PI; - /// - /// let abs_difference = (angle.to_degrees() - 180.0).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn to_degrees(self) -> f64 { num::Float::to_degrees(self) } - - /// Converts degrees to radians. - /// - /// ``` - /// use std::f64::consts; - /// - /// let angle = 180.0_f64; - /// - /// let abs_difference = (angle.to_radians() - consts::PI).abs(); - /// - /// assert!(abs_difference < 1e-10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn to_radians(self) -> f64 { num::Float::to_radians(self) } - - /// Returns the maximum of the two numbers. - /// - /// ``` - /// let x = 1.0_f64; - /// let y = 2.0_f64; - /// - /// assert_eq!(x.max(y), y); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn max(self, other: f64) -> f64 { - num::Float::max(self, other) - } - - /// Returns the minimum of the two numbers. - /// - /// ``` - /// let x = 1.0_f64; - /// let y = 2.0_f64; - /// - /// assert_eq!(x.min(y), x); - /// ``` - /// - /// If one of the arguments is NaN, then the other argument is returned. - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn min(self, other: f64) -> f64 { - num::Float::min(self, other) - } - /// The positive difference of two numbers. /// /// * If `self <= other`: `0:0` @@ -1000,73 +794,6 @@ impl f64 { } } } - - /// Raw transmutation to `u64`. - /// - /// This is currently identical to `transmute::(self)` on all platforms. - /// - /// See `from_bits` for some discussion of the portability of this operation - /// (there are almost no issues). - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// assert!((1f64).to_bits() != 1f64 as u64); // to_bits() is not casting! - /// assert_eq!((12.5f64).to_bits(), 0x4029000000000000); - /// - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn to_bits(self) -> u64 { - num::Float::to_bits(self) - } - - /// Raw transmutation from `u64`. - /// - /// This is currently identical to `transmute::(v)` on all platforms. - /// It turns out this is incredibly portable, for two reasons: - /// - /// * Floats and Ints have the same endianness on all supported platforms. - /// * IEEE-754 very precisely specifies the bit layout of floats. - /// - /// However there is one caveat: prior to the 2008 version of IEEE-754, how - /// to interpret the NaN signaling bit wasn't actually specified. Most platforms - /// (notably x86 and ARM) picked the interpretation that was ultimately - /// standardized in 2008, but some didn't (notably MIPS). As a result, all - /// signaling NaNs on MIPS are quiet NaNs on x86, and vice-versa. - /// - /// Rather than trying to preserve signaling-ness cross-platform, this - /// implementation favours preserving the exact bits. This means that - /// any payloads encoded in NaNs will be preserved even if the result of - /// this method is sent over the network from an x86 machine to a MIPS one. - /// - /// If the results of this method are only manipulated by the same - /// architecture that produced them, then there is no portability concern. - /// - /// If the input isn't NaN, then there is no portability concern. - /// - /// If you don't care about signalingness (very likely), then there is no - /// portability concern. - /// - /// Note that this function is distinct from `as` casting, which attempts to - /// preserve the *numeric* value, and not the bitwise value. - /// - /// # Examples - /// - /// ``` - /// use std::f64; - /// let v = f64::from_bits(0x4029000000000000); - /// let difference = (v - 12.5).abs(); - /// assert!(difference <= 1e-5); - /// ``` - #[stable(feature = "float_bits_conv", since = "1.20.0")] - #[inline] - pub fn from_bits(v: u64) -> Self { - num::Float::from_bits(v) - } } #[cfg(test)] -- cgit 1.4.1-3-g733a5 From 70fdd1b5c0f6a0673fcf924b3d8880af034bdee0 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 12 Apr 2018 08:36:31 +0200 Subject: Make the unstable StrExt and SliceExt traits private to libcore in not(stage0) `Float` still needs to be public for libcore unit tests. --- src/liballoc/lib.rs | 2 +- src/libcore/internal_macros.rs | 13 +++++++++ src/libcore/num/mod.rs | 31 +++++++++------------- src/libcore/slice/mod.rs | 7 +++-- src/libcore/str/mod.rs | 8 +++--- src/libcore/tests/lib.rs | 1 + src/libstd/lib.rs | 3 ++- .../method-suggestion-no-duplication.stderr | 4 +-- 8 files changed, 40 insertions(+), 29 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 702d7b70cd3..6399be98cd5 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -75,7 +75,7 @@ #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(not(test), feature(core_float))] +#![cfg_attr(all(not(test), stage0), feature(float_internals))] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] diff --git a/src/libcore/internal_macros.rs b/src/libcore/internal_macros.rs index cb215a38e53..58eef649287 100644 --- a/src/libcore/internal_macros.rs +++ b/src/libcore/internal_macros.rs @@ -87,3 +87,16 @@ macro_rules! forward_ref_op_assign { } } +#[cfg(stage0)] +macro_rules! public_in_stage0 { + ( { $(#[$attr:meta])* } $($Item: tt)*) => { + $(#[$attr])* pub $($Item)* + } +} + +#[cfg(not(stage0))] +macro_rules! public_in_stage0 { + ( { $(#[$attr:meta])* } $($Item: tt)*) => { + $(#[$attr])* pub(crate) $($Item)* + } +} diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 55fad62d871..70c704267d2 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -4098,65 +4098,58 @@ pub enum FpCategory { Normal, } -/// A built-in floating point number. +// Technically private and only exposed for coretests: #[doc(hidden)] -#[unstable(feature = "core_float", - reason = "stable interface is via `impl f{32,64}` in later crates", - issue = "32110")] +#[unstable(feature = "float_internals", + reason = "internal routines only exposed for testing", + issue = "0")] pub trait Float: Sized { /// Type used by `to_bits` and `from_bits`. - #[stable(feature = "core_float_bits", since = "1.25.0")] type Bits; /// Returns `true` if this value is NaN and false otherwise. - #[stable(feature = "core", since = "1.6.0")] fn is_nan(self) -> bool; + /// Returns `true` if this value is positive infinity or negative infinity and /// false otherwise. - #[stable(feature = "core", since = "1.6.0")] fn is_infinite(self) -> bool; + /// Returns `true` if this number is neither infinite nor NaN. - #[stable(feature = "core", since = "1.6.0")] fn is_finite(self) -> bool; + /// Returns `true` if this number is neither zero, infinite, denormal, or NaN. - #[stable(feature = "core", since = "1.6.0")] fn is_normal(self) -> bool; + /// Returns the category that this number falls into. - #[stable(feature = "core", since = "1.6.0")] fn classify(self) -> FpCategory; /// Returns `true` if `self` is positive, including `+0.0` and /// `Float::infinity()`. - #[stable(feature = "core", since = "1.6.0")] fn is_sign_positive(self) -> bool; + /// Returns `true` if `self` is negative, including `-0.0` and /// `Float::neg_infinity()`. - #[stable(feature = "core", since = "1.6.0")] fn is_sign_negative(self) -> bool; /// Take the reciprocal (inverse) of a number, `1/x`. - #[stable(feature = "core", since = "1.6.0")] fn recip(self) -> Self; /// Convert radians to degrees. - #[stable(feature = "deg_rad_conversions", since="1.7.0")] fn to_degrees(self) -> Self; + /// Convert degrees to radians. - #[stable(feature = "deg_rad_conversions", since="1.7.0")] fn to_radians(self) -> Self; /// Returns the maximum of the two numbers. - #[stable(feature = "core_float_min_max", since="1.20.0")] fn max(self, other: Self) -> Self; + /// Returns the minimum of the two numbers. - #[stable(feature = "core_float_min_max", since="1.20.0")] fn min(self, other: Self) -> Self; /// Raw transmutation to integer. - #[stable(feature = "core_float_bits", since="1.25.0")] fn to_bits(self) -> Self::Bits; + /// Raw transmutation from integer. - #[stable(feature = "core_float_bits", since="1.25.0")] fn from_bits(v: Self::Bits) -> Self; } diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 0a260c663c2..cc42acd77ae 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -68,12 +68,15 @@ struct Repr { // Extension traits // +public_in_stage0! { +{ /// Extension methods for slices. #[unstable(feature = "core_slice_ext", reason = "stable interface provided by `impl [T]` in later crates", issue = "32110")] #[allow(missing_docs)] // documented elsewhere -pub trait SliceExt { +} +trait SliceExt { type Item; #[stable(feature = "core", since = "1.6.0")] @@ -238,7 +241,7 @@ pub trait SliceExt { fn sort_unstable_by_key(&mut self, f: F) where F: FnMut(&Self::Item) -> B, B: Ord; -} +}} // Use macros to be generic over const/mut macro_rules! slice_offset { diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index d33eaace79d..a76de79107b 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2117,14 +2117,16 @@ mod traits { } - +public_in_stage0! { +{ /// Methods for string slices #[allow(missing_docs)] #[doc(hidden)] #[unstable(feature = "core_str_ext", reason = "stable interface provided by `impl str` in later crates", issue = "32110")] -pub trait StrExt { +} +trait StrExt { // NB there are no docs here are they're all located on the StrExt trait in // liballoc, not here. @@ -2224,7 +2226,7 @@ pub trait StrExt { fn trim_left(&self) -> &str; #[stable(feature = "rust1", since = "1.0.0")] fn trim_right(&self) -> &str; -} +}} // truncate `&str` to length at most equal to `max` // return `true` if it were truncated, and the new str. diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index bb875c7219a..2cc2ac289bf 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -17,6 +17,7 @@ #![feature(decode_utf8)] #![feature(exact_size_is_empty)] #![feature(fixed_size_array)] +#![feature(float_internals)] #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(hashmap_internals)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7d896695311..3b98abb9293 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -252,7 +252,7 @@ #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] -#![feature(core_float)] +#![cfg_attr(stage0, feature(core_float))] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] @@ -260,6 +260,7 @@ #![feature(fs_read_write)] #![feature(fixed_size_array)] #![feature(float_from_str_radix)] +#![cfg_attr(stage0, feature(float_internals))] #![feature(fn_traits)] #![feature(fnbox)] #![cfg_attr(stage0, feature(generic_param_attrs))] diff --git a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr index 438d29f0535..f7aaab4242c 100644 --- a/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr +++ b/src/test/ui/impl-trait/method-suggestion-no-duplication.stderr @@ -8,10 +8,8 @@ LL | foo(|s| s.is_empty()); | ^^^^^^^^ | = help: items from traits can only be used if the trait is implemented and in scope - = note: the following traits define an item `is_empty`, perhaps you need to implement one of them: + = note: the following trait defines an item `is_empty`, perhaps you need to implement it: candidate #1: `std::iter::ExactSizeIterator` - candidate #2: `core::slice::SliceExt` - candidate #3: `core::str::StrExt` error: aborting due to previous error -- cgit 1.4.1-3-g733a5 From e513c1bd314bbeb6295a7a759de8833b52ff854d Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Fri, 20 Apr 2018 21:05:13 -0700 Subject: Replace GlobalAlloc::oom with a lang item --- src/liballoc/alloc.rs | 35 ++++++++++++++++---------------- src/liballoc/arc.rs | 4 ++-- src/liballoc/rc.rs | 4 ++-- src/liballoc_jemalloc/lib.rs | 5 ++--- src/liballoc_system/lib.rs | 2 +- src/libcore/alloc.rs | 11 ---------- src/librustc/middle/lang_items.rs | 3 ++- src/librustc/middle/weak_lang_items.rs | 1 + src/librustc_allocator/lib.rs | 5 ----- src/libstd/alloc.rs | 13 ++++++++++-- src/libstd/collections/hash/map.rs | 4 ++-- src/libstd/collections/hash/table.rs | 6 +++--- src/libstd/lib.rs | 3 ++- src/test/run-pass/allocator-alloc-one.rs | 6 ++---- src/test/run-pass/realloc-16687.rs | 6 +++--- src/test/run-pass/regions-mock-trans.rs | 4 ++-- 16 files changed, 53 insertions(+), 59 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 68a617e0ffe..a9c06523718 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -48,9 +48,6 @@ extern "Rust" { #[allocator] #[rustc_allocator_nounwind] fn __rust_alloc(size: usize, align: usize) -> *mut u8; - #[cold] - #[rustc_allocator_nounwind] - fn __rust_oom() -> !; #[rustc_allocator_nounwind] fn __rust_dealloc(ptr: *mut u8, size: usize, align: usize); #[rustc_allocator_nounwind] @@ -107,16 +104,6 @@ unsafe impl GlobalAlloc for Global { let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } - - #[inline] - fn oom(&self) -> ! { - unsafe { - #[cfg(not(stage0))] - __rust_oom(); - #[cfg(stage0)] - __rust_oom(&mut 0); - } - } } unsafe impl Alloc for Global { @@ -147,7 +134,7 @@ unsafe impl Alloc for Global { #[inline] fn oom(&mut self) -> ! { - GlobalAlloc::oom(self) + oom() } } @@ -165,7 +152,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr as *mut u8 } else { - Global.oom() + oom() } } } @@ -182,19 +169,33 @@ pub(crate) unsafe fn box_free(ptr: *mut T) { } } +#[cfg(stage0)] +pub fn oom() -> ! { + unsafe { ::core::intrinsics::abort() } +} + +#[cfg(not(stage0))] +pub fn oom() -> ! { + extern { + #[lang = "oom"] + fn oom_impl() -> !; + } + unsafe { oom_impl() } +} + #[cfg(test)] mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use alloc::{Global, Alloc, Layout}; + use alloc::{Global, Alloc, Layout, oom}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 225b055d8ee..f5980f4599e 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -31,7 +31,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free}; +use alloc::{Global, Alloc, Layout, box_free, oom}; use boxed::Box; use string::String; use vec::Vec; @@ -553,7 +553,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index de0422d82bb..8fb8e111754 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Opaque, box_free}; +use alloc::{Global, Alloc, Layout, Opaque, box_free, oom}; use string::String; use vec::Vec; @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 2b66c293f21..8b118a2cabb 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -30,8 +30,6 @@ extern crate libc; pub use contents::*; #[cfg(not(dummy_jemalloc))] mod contents { - use core::alloc::GlobalAlloc; - use alloc_system::System; use libc::{c_int, c_void, size_t}; // Note that the symbols here are prefixed by default on macOS and Windows (we @@ -100,10 +98,11 @@ mod contents { ptr } + #[cfg(stage0)] #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_oom() -> ! { - System.oom() + ::alloc_system::oom() } #[no_mangle] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index fd8109e2a4a..aff98ae2f10 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -368,7 +368,7 @@ mod platform { } #[inline] -fn oom() -> ! { +pub fn oom() -> ! { write_to_stderr("fatal runtime error: memory allocation failed"); unsafe { ::core::intrinsics::abort(); diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 54440eaa40f..7d893676a6c 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -451,17 +451,6 @@ pub unsafe trait GlobalAlloc { } new_ptr } - - /// Aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request, and wish to abandon - /// computation rather than attempt to recover locally. - fn oom(&self) -> ! { - unsafe { ::intrinsics::abort() } - } } /// An implementation of `Alloc` can allocate, reallocate, and diff --git a/src/librustc/middle/lang_items.rs b/src/librustc/middle/lang_items.rs index c7412dbeeb3..95e92e21b09 100644 --- a/src/librustc/middle/lang_items.rs +++ b/src/librustc/middle/lang_items.rs @@ -303,7 +303,8 @@ language_item_table! { ExchangeMallocFnLangItem, "exchange_malloc", exchange_malloc_fn; BoxFreeFnLangItem, "box_free", box_free_fn; - DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; + DropInPlaceFnLangItem, "drop_in_place", drop_in_place_fn; + OomLangItem, "oom", oom; StartFnLangItem, "start", start_fn; diff --git a/src/librustc/middle/weak_lang_items.rs b/src/librustc/middle/weak_lang_items.rs index e19f4483f65..a2bceb19102 100644 --- a/src/librustc/middle/weak_lang_items.rs +++ b/src/librustc/middle/weak_lang_items.rs @@ -151,4 +151,5 @@ weak_lang_items! { panic_fmt, PanicFmtLangItem, rust_begin_unwind; eh_personality, EhPersonalityLangItem, rust_eh_personality; eh_unwind_resume, EhUnwindResumeLangItem, rust_eh_unwind_resume; + oom, OomLangItem, rust_oom; } diff --git a/src/librustc_allocator/lib.rs b/src/librustc_allocator/lib.rs index 706eab72d44..f3103e21606 100644 --- a/src/librustc_allocator/lib.rs +++ b/src/librustc_allocator/lib.rs @@ -23,11 +23,6 @@ pub static ALLOCATOR_METHODS: &[AllocatorMethod] = &[ inputs: &[AllocatorTy::Layout], output: AllocatorTy::ResultPtr, }, - AllocatorMethod { - name: "oom", - inputs: &[], - output: AllocatorTy::Bang, - }, AllocatorMethod { name: "dealloc", inputs: &[AllocatorTy::Ptr, AllocatorTy::Layout], diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ff578ec42d2..a8578404467 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -13,10 +13,18 @@ #![unstable(issue = "32838", feature = "allocator_api")] #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; -#[doc(inline)] pub use alloc_crate::alloc::Global; +#[doc(inline)] pub use alloc_crate::alloc::{Global, oom}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; +#[cfg(not(stage0))] +#[cfg(not(test))] +#[doc(hidden)] +#[lang = "oom"] +pub extern fn rust_oom() -> ! { + rtabort!("memory allocation failed"); +} + #[cfg(not(test))] #[doc(hidden)] #[allow(unused_attributes)] @@ -35,10 +43,11 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } + #[cfg(stage0)] #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_oom() -> ! { - System.oom() + super::oom() } #[no_mangle] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 4fe5c11beb4..a8c70489f44 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,7 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::{Global, Alloc, CollectionAllocErr}; +use alloc::{CollectionAllocErr, oom}; use cell::Cell; use borrow::Borrow; use cmp::max; @@ -784,7 +784,7 @@ impl HashMap pub fn reserve(&mut self, additional: usize) { match self.try_reserve(additional) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(()) => { /* yay */ } } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 115f9628a23..52c53dc3b12 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, CollectionAllocErr}; +use alloc::{Global, Alloc, Layout, CollectionAllocErr, oom}; use cmp; use hash::{BuildHasher, Hash, Hasher}; use marker; @@ -770,7 +770,7 @@ impl RawTable { unsafe fn new_uninitialized(capacity: usize) -> RawTable { match Self::try_new_uninitialized(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(table) => { table } } } @@ -809,7 +809,7 @@ impl RawTable { pub fn new(capacity: usize) -> RawTable { match Self::try_new(capacity) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => Global.oom(), + Err(CollectionAllocErr::AllocErr) => oom(), Ok(table) => { table } } } diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 43a8d4446fa..1df7bc777d1 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -482,7 +482,6 @@ pub mod path; pub mod process; pub mod sync; pub mod time; -pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] @@ -496,6 +495,8 @@ pub mod heap { mod sys_common; mod sys; +pub mod alloc; + // Private support modules mod panicking; mod memchr; diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index d4fcdcf743b..12b115d0938 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,13 +10,11 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global}; +use std::alloc::{Alloc, Global, oom}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| { - Global.oom() - }); + let ptr = Global.alloc_one::().unwrap_or_else(|_| oom()); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index afa3494c389..308792e5d89 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -15,7 +15,7 @@ #![feature(heap_api, allocator_api)] -use std::alloc::{Global, Alloc, Layout}; +use std::alloc::{Global, Alloc, Layout, oom}; use std::ptr::{self, NonNull}; fn main() { @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| Global.oom()); + let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| oom()); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,7 @@ unsafe fn test_triangle() -> bool { } let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs index 44be59f5c5b..60a7f70931d 100644 --- a/src/test/run-pass/regions-mock-trans.rs +++ b/src/test/run-pass/regions-mock-trans.rs @@ -12,7 +12,7 @@ #![feature(allocator_api)] -use std::alloc::{Alloc, Global, Layout}; +use std::alloc::{Alloc, Global, Layout, oom}; use std::ptr::NonNull; struct arena(()); @@ -33,7 +33,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| Global.oom()); + .unwrap_or_else(|_| oom()); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From 9e8f683476d5a9d72c6c1e9383a519cf0fb27494 Mon Sep 17 00:00:00 2001 From: Steven Fackler Date: Sat, 21 Apr 2018 09:47:41 -0700 Subject: Remove Alloc::oom --- src/Cargo.lock | 1 - src/liballoc/alloc.rs | 5 -- src/liballoc/heap.rs | 2 +- src/liballoc/raw_vec.rs | 14 ++--- src/liballoc_jemalloc/Cargo.toml | 1 - src/liballoc_jemalloc/lib.rs | 5 +- src/liballoc_system/lib.rs | 70 ---------------------- src/libcore/alloc.rs | 26 -------- .../compile-fail/allocator/not-an-allocator.rs | 1 - 9 files changed, 10 insertions(+), 115 deletions(-) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index d7d23f0fa80..6f054dc61a5 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -19,7 +19,6 @@ dependencies = [ name = "alloc_jemalloc" version = "0.0.0" dependencies = [ - "alloc_system 0.0.0", "build_helper 0.1.0", "cc 1.0.10 (registry+https://github.com/rust-lang/crates.io-index)", "compiler_builtins 0.0.0", diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index a9c06523718..c0372d24ed5 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -131,11 +131,6 @@ unsafe impl Alloc for Global { unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - oom() - } } /// The allocator for unique pointers. diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index faac38ca7ce..16f0630b911 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -59,7 +59,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } fn oom(&mut self, _: AllocErr) -> ! { - CoreAlloc::oom(self) + unsafe { ::core::intrinsics::abort() } } fn usable_size(&self, layout: &Layout) -> (usize, usize) { diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 24b7cd3db0c..7ef0a27fc72 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -14,7 +14,7 @@ use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use alloc::{Alloc, Layout, Global}; +use alloc::{Alloc, Layout, Global, oom}; use alloc::CollectionAllocErr; use alloc::CollectionAllocErr::*; use boxed::Box; @@ -101,7 +101,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr, - Err(_) => a.oom(), + Err(_) => oom(), } }; @@ -316,7 +316,7 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => self.a.oom(), + Err(_) => oom(), } } None => { @@ -325,7 +325,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => self.a.oom(), + Err(_) => oom(), } } }; @@ -442,7 +442,7 @@ impl RawVec { pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => self.a.oom(), + Err(AllocErr) => oom(), Ok(()) => { /* yay */ } } } @@ -552,7 +552,7 @@ impl RawVec { pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => self.a.oom(), + Err(AllocErr) => oom(), Ok(()) => { /* yay */ } } } @@ -667,7 +667,7 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => self.a.oom(), + Err(_) => oom(), } } self.cap = amount; diff --git a/src/liballoc_jemalloc/Cargo.toml b/src/liballoc_jemalloc/Cargo.toml index 02435170374..7986d5dd2eb 100644 --- a/src/liballoc_jemalloc/Cargo.toml +++ b/src/liballoc_jemalloc/Cargo.toml @@ -12,7 +12,6 @@ test = false doc = false [dependencies] -alloc_system = { path = "../liballoc_system" } core = { path = "../libcore" } libc = { path = "../rustc/libc_shim" } compiler_builtins = { path = "../rustc/compiler_builtins_shim" } diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 8b118a2cabb..4b8755877de 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -14,7 +14,7 @@ reason = "this library is unlikely to be stabilized in its current \ form or name", issue = "27783")] -#![feature(alloc_system)] +#![feature(core_intrinsics)] #![feature(libc)] #![feature(linkage)] #![feature(staged_api)] @@ -23,7 +23,6 @@ #![cfg_attr(not(dummy_jemalloc), feature(allocator_api))] #![rustc_alloc_kind = "exe"] -extern crate alloc_system; extern crate libc; #[cfg(not(dummy_jemalloc))] @@ -102,7 +101,7 @@ mod contents { #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_oom() -> ! { - ::alloc_system::oom() + ::core::intrinsics::abort(); } #[no_mangle] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index aff98ae2f10..7376ac0f15d 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -71,11 +71,6 @@ unsafe impl Alloc for System { new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - ::oom() - } } #[cfg(stage0)] @@ -103,11 +98,6 @@ unsafe impl<'a> Alloc for &'a System { new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } - - #[inline] - fn oom(&mut self) -> ! { - ::oom() - } } #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] @@ -366,63 +356,3 @@ mod platform { } } } - -#[inline] -pub fn oom() -> ! { - write_to_stderr("fatal runtime error: memory allocation failed"); - unsafe { - ::core::intrinsics::abort(); - } -} - -#[cfg(any(unix, target_os = "redox"))] -#[inline] -fn write_to_stderr(s: &str) { - extern crate libc; - - unsafe { - libc::write(libc::STDERR_FILENO, - s.as_ptr() as *const libc::c_void, - s.len()); - } -} - -#[cfg(windows)] -#[inline] -fn write_to_stderr(s: &str) { - use core::ptr; - - type LPVOID = *mut u8; - type HANDLE = LPVOID; - type DWORD = u32; - type BOOL = i32; - type LPDWORD = *mut DWORD; - type LPOVERLAPPED = *mut u8; - - const STD_ERROR_HANDLE: DWORD = -12i32 as DWORD; - - extern "system" { - fn WriteFile(hFile: HANDLE, - lpBuffer: LPVOID, - nNumberOfBytesToWrite: DWORD, - lpNumberOfBytesWritten: LPDWORD, - lpOverlapped: LPOVERLAPPED) - -> BOOL; - fn GetStdHandle(which: DWORD) -> HANDLE; - } - - unsafe { - // WriteFile silently fails if it is passed an invalid - // handle, so there is no need to check the result of - // GetStdHandle. - WriteFile(GetStdHandle(STD_ERROR_HANDLE), - s.as_ptr() as LPVOID, - s.len() as DWORD, - ptr::null_mut(), - ptr::null_mut()); - } -} - -#[cfg(not(any(windows, unix, target_os = "redox")))] -#[inline] -fn write_to_stderr(_: &str) {} diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 7d893676a6c..674c4fb57c7 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -603,32 +603,6 @@ pub unsafe trait Alloc { /// to allocate that block of memory. unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); - /// Allocator-specific method for signaling an out-of-memory - /// condition. - /// - /// `oom` aborts the thread or process, optionally performing - /// cleanup or logging diagnostic information before panicking or - /// aborting. - /// - /// `oom` is meant to be used by clients unable to cope with an - /// unsatisfied allocation request, and wish to abandon - /// computation rather than attempt to recover locally. - /// - /// Implementations of the `oom` method are discouraged from - /// infinitely regressing in nested calls to `oom`. In - /// practice this means implementors should eschew allocating, - /// especially from `self` (directly or indirectly). - /// - /// Implementations of the allocation and reallocation methods - /// (e.g. `alloc`, `alloc_one`, `realloc`) are discouraged from - /// panicking (or aborting) in the event of memory exhaustion; - /// instead they should return an appropriate error from the - /// invoked method, and let the client decide whether to invoke - /// this `oom` method in response. - fn oom(&mut self) -> ! { - unsafe { ::intrinsics::abort() } - } - // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == // usable_size diff --git a/src/test/compile-fail/allocator/not-an-allocator.rs b/src/test/compile-fail/allocator/not-an-allocator.rs index 1479d0b6264..140cad22f34 100644 --- a/src/test/compile-fail/allocator/not-an-allocator.rs +++ b/src/test/compile-fail/allocator/not-an-allocator.rs @@ -16,6 +16,5 @@ static A: usize = 0; //~| the trait bound `usize: //~| the trait bound `usize: //~| the trait bound `usize: -//~| the trait bound `usize: fn main() {} -- cgit 1.4.1-3-g733a5 From 1133a149f1bd89bbc9303e3514a6bc20c3a5fc8b Mon Sep 17 00:00:00 2001 From: George Burton Date: Sun, 22 Apr 2018 22:57:52 +0100 Subject: Implement From for more types on Cow --- src/liballoc/string.rs | 8 ++++++++ src/liballoc/vec.rs | 7 +++++++ src/libstd/ffi/c_str.rs | 24 ++++++++++++++++++++++++ src/libstd/ffi/os_str.rs | 24 ++++++++++++++++++++++++ src/libstd/path.rs | 8 ++++++++ 5 files changed, 71 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 2f84d5f7f86..5f684361fdc 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2239,6 +2239,14 @@ impl<'a> From for Cow<'a, str> { } } +#[stable(feature = "cow_from_string_ref", since = "1.28.0")] +impl<'a> From<&'a String> for Cow<'a, str> { + #[inline] + fn from(s: &'a String) -> Cow<'a, str> { + Cow::Borrowed(s.as_str()) + } +} + #[stable(feature = "cow_str_from_iter", since = "1.12.0")] impl<'a> FromIterator for Cow<'a, str> { fn from_iter>(it: I) -> Cow<'a, str> { diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index b184404c15b..be4c80c85d9 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2285,6 +2285,13 @@ impl<'a, T: Clone> From> for Cow<'a, [T]> { } } +#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] +impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { + fn from(v: &'a Vec) -> Cow<'a, [T]> { + Cow::Borrowed(v.as_slice()) + } +} + #[stable(feature = "rust1", since = "1.0.0")] impl<'a, T> FromIterator for Cow<'a, [T]> where T: Clone { fn from_iter>(it: I) -> Cow<'a, [T]> { diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index c88c2bc9137..08a1596ef25 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -706,6 +706,30 @@ impl From for Box { } } +#[stable(feature = "cow_from_cstr", since = "1.28.0")] +impl<'a> From for Cow<'a, CStr> { + #[inline] + fn from(s: CString) -> Cow<'a, CStr> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_cstr", since = "1.28.0")] +impl<'a> From<&'a CStr> for Cow<'a, CStr> { + #[inline] + fn from(s: &'a CStr) -> Cow<'a, CStr> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_cstr", since = "1.28.0")] +impl<'a> From<&'a CString> for Cow<'a, CStr> { + #[inline] + fn from(s: &'a CString) -> Cow<'a, CStr> { + Cow::Borrowed(s.as_c_str()) + } +} + #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { #[inline] diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 4850ed0c5be..e42a28ed88f 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -664,6 +664,30 @@ impl<'a> From<&'a OsStr> for Rc { } } +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From for Cow<'a, OsStr> { + #[inline] + fn from(s: OsString) -> Cow<'a, OsStr> { + Cow::Owned(s) + } +} + +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { + #[inline] + fn from(s: &'a OsStr) -> Cow<'a, OsStr> { + Cow::Borrowed(s) + } +} + +#[stable(feature = "cow_from_osstr", since = "1.28.0")] +impl<'a> From<&'a OsString> for Cow<'a, OsStr> { + #[inline] + fn from(s: &'a OsString) -> Cow<'a, OsStr> { + Cow::Borrowed(s.as_os_str()) + } +} + #[stable(feature = "box_default_extra", since = "1.17.0")] impl Default for Box { fn default() -> Box { diff --git a/src/libstd/path.rs b/src/libstd/path.rs index ec961575473..19f38e4d6d9 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1532,6 +1532,14 @@ impl<'a> From for Cow<'a, Path> { } } +#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] +impl<'a> From<&'a PathBuf> for Cow<'a, Path> { + #[inline] + fn from(p: &'a PathBuf) -> Cow<'a, Path> { + Cow::Borrowed(p.as_path()) + } +} + #[stable(feature = "shared_from_slice2", since = "1.24.0")] impl From for Arc { #[inline] -- cgit 1.4.1-3-g733a5 From 5d37ba1990bdc552694383d2a3c6cbeb72b77f49 Mon Sep 17 00:00:00 2001 From: Matthias Krüger Date: Mon, 23 Apr 2018 13:04:37 +0200 Subject: mark std::str::replacen and std::str::replace as #[must_use]. --- src/liballoc/lib.rs | 1 + src/liballoc/str.rs | 2 ++ 2 files changed, 3 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 6399be98cd5..021395d0c82 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -96,6 +96,7 @@ #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] +#![feature(fn_must_use)] #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 82ba2f45711..e8c6b964c70 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -207,6 +207,7 @@ impl str { /// let s = "this is old"; /// assert_eq!(s, s.replace("cookie monster", "little lamb")); /// ``` + #[must_use] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String { @@ -246,6 +247,7 @@ impl str { /// let s = "this is old"; /// assert_eq!(s, s.replacen("cookie monster", "little lamb", 10)); /// ``` + #[must_use] #[stable(feature = "str_replacen", since = "1.16.0")] pub fn replacen<'a, P: Pattern<'a>>(&'a self, pat: P, to: &str, count: usize) -> String { // Hope to reduce the times of re-allocation -- cgit 1.4.1-3-g733a5 From bd8c177d49c95d94f163e9bb3c3397f38ab72640 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Fri, 20 Apr 2018 10:24:53 +0900 Subject: Switch box_free to take the destructured contents of Box As of now, Box only contains a Unique pointer, so this is the sole argument to box_free. Consequently, we remove the code supporting the previous box_free signature. We however keep the old definition for bootstrapping purpose. --- src/liballoc/alloc.rs | 14 ++++++-- src/liballoc/arc.rs | 5 +-- src/liballoc/rc.rs | 5 +-- src/librustc_mir/util/elaborate_drops.rs | 61 +++++++------------------------- 4 files changed, 29 insertions(+), 56 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 68a617e0ffe..ed860678765 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -16,7 +16,7 @@ issue = "32838")] use core::intrinsics::{min_align_of_val, size_of_val}; -use core::ptr::NonNull; +use core::ptr::{NonNull, Unique}; use core::usize; #[doc(inline)] @@ -170,9 +170,17 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg_attr(not(test), lang = "box_free")] +#[cfg(stage0)] +#[lang = "box_free"] +#[inline] +unsafe fn old_box_free(ptr: *mut T) { + box_free(Unique::new_unchecked(ptr)) +} + +#[cfg_attr(not(any(test, stage0)), lang = "box_free")] #[inline] -pub(crate) unsafe fn box_free(ptr: *mut T) { +pub(crate) unsafe fn box_free(ptr: Unique) { + let ptr = ptr.as_ptr(); let size = size_of_val(&*ptr); let align = min_align_of_val(&*ptr); // We do not allocate for Box when T is ZST, so deallocation is also not necessary. diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 225b055d8ee..a1ec5cd2208 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -566,7 +566,8 @@ impl Arc { fn from_box(v: Box) -> Arc { unsafe { - let bptr = Box::into_raw(v); + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); let ptr = Self::allocate_for_ptr(bptr); @@ -578,7 +579,7 @@ impl Arc { value_size); // Free the allocation without dropping its contents - box_free(bptr); + box_free(box_unique); Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index de0422d82bb..c495d300805 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -681,7 +681,8 @@ impl Rc { fn from_box(v: Box) -> Rc { unsafe { - let bptr = Box::into_raw(v); + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); let value_size = size_of_val(&*bptr); let ptr = Self::allocate_for_ptr(bptr); @@ -693,7 +694,7 @@ impl Rc { value_size); // Free the allocation without dropping its contents - box_free(bptr); + box_free(box_unique); Rc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } } diff --git a/src/librustc_mir/util/elaborate_drops.rs b/src/librustc_mir/util/elaborate_drops.rs index cc91bbf9061..02020c3b7a4 100644 --- a/src/librustc_mir/util/elaborate_drops.rs +++ b/src/librustc_mir/util/elaborate_drops.rs @@ -879,56 +879,19 @@ impl<'l, 'b, 'tcx, D> DropCtxt<'l, 'b, 'tcx, D> let tcx = self.tcx(); let unit_temp = Place::Local(self.new_temp(tcx.mk_nil())); let free_func = tcx.require_lang_item(lang_items::BoxFreeFnLangItem); - let free_sig = tcx.fn_sig(free_func).skip_binder().clone(); - let free_inputs = free_sig.inputs(); - // If the box_free function takes a *mut T, transform the Box into - // such a pointer before calling box_free. Otherwise, pass it all - // the fields in the Box as individual arguments. - let (stmts, args) = if free_inputs.len() == 1 && free_inputs[0].is_mutable_pointer() { - let ty = substs.type_at(0); - let ref_ty = tcx.mk_ref(tcx.types.re_erased, ty::TypeAndMut { - ty: ty, - mutbl: hir::Mutability::MutMutable - }); - let ptr_ty = tcx.mk_mut_ptr(ty); - let ref_tmp = Place::Local(self.new_temp(ref_ty)); - let ptr_tmp = Place::Local(self.new_temp(ptr_ty)); - let stmts = vec![ - self.assign(&ref_tmp, Rvalue::Ref( - tcx.types.re_erased, - BorrowKind::Mut { allow_two_phase_borrow: false }, - self.place.clone().deref() - )), - self.assign(&ptr_tmp, Rvalue::Cast( - CastKind::Misc, - Operand::Move(ref_tmp), - ptr_ty, - )), - ]; - (stmts, vec![Operand::Move(ptr_tmp)]) - } else { - let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| { - let field = Field::new(i); - let field_ty = f.ty(self.tcx(), substs); - Operand::Move(self.place.clone().field(field, field_ty)) - }).collect(); - (vec![], args) - }; + let args = adt.variants[0].fields.iter().enumerate().map(|(i, f)| { + let field = Field::new(i); + let field_ty = f.ty(self.tcx(), substs); + Operand::Move(self.place.clone().field(field, field_ty)) + }).collect(); - let free_block = BasicBlockData { - statements: stmts, - terminator: Some(Terminator { - kind: TerminatorKind::Call { - func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), - args: args, - destination: Some((unit_temp, target)), - cleanup: None - }, // FIXME(#43234) - source_info: self.source_info, - }), - is_cleanup: unwind.is_cleanup() - }; - let free_block = self.elaborator.patch().new_block(free_block); + let call = TerminatorKind::Call { + func: Operand::function_handle(tcx, free_func, substs, self.source_info.span), + args: args, + destination: Some((unit_temp, target)), + cleanup: None + }; // FIXME(#43234) + let free_block = self.new_block(unwind, call); let block_start = Location { block: free_block, statement_index: 0 }; self.elaborator.clear_drop_flag(block_start, self.path, DropFlagMode::Shallow); -- cgit 1.4.1-3-g733a5 From 1bcb267651c2f4a840e928d9309c240d146a335b Mon Sep 17 00:00:00 2001 From: Ralf Biedert Date: Wed, 25 Apr 2018 14:14:43 +0200 Subject: Added missing `.` in docs. --- src/liballoc/arc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index f5980f4599e..e52a0216dd3 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -60,7 +60,7 @@ const MAX_REFCOUNT: usize = (isize::MAX) as usize; /// ## Thread Safety /// /// Unlike [`Rc`], `Arc` uses atomic operations for its reference -/// counting This means that it is thread-safe. The disadvantage is that +/// counting. This means that it is thread-safe. The disadvantage is that /// atomic operations are more expensive than ordinary memory accesses. If you /// are not sharing reference-counted values between threads, consider using /// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the -- cgit 1.4.1-3-g733a5 From 256096da9ee680366b839f912e8d3ecccc0da033 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Wed, 25 Apr 2018 16:33:02 -0500 Subject: Make Vec::new const --- src/liballoc/raw_vec.rs | 16 ++++++++++++++++ src/liballoc/vec.rs | 2 +- src/libcore/ptr.rs | 5 ++--- src/test/run-pass/vec-const-new.rs | 15 +++++++++++++++ 4 files changed, 34 insertions(+), 4 deletions(-) create mode 100644 src/test/run-pass/vec-const-new.rs (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 7ef0a27fc72..dc8ad9ee061 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -68,6 +68,16 @@ impl RawVec { } } + /// Like `empty` but parametrized over the choice of allocator for the returned `RawVec`. + pub const fn empty_in(a: A) -> Self { + // Unique::empty() doubles as "unallocated" and "zero-sized allocation" + RawVec { + ptr: Unique::empty(), + cap: 0, + a, + } + } + /// Like `with_capacity` but parameterized over the choice of /// allocator for the returned RawVec. #[inline] @@ -124,6 +134,12 @@ impl RawVec { Self::new_in(Global) } + /// Create a `RawVec` with capcity 0 (on the system heap), regardless of `T`, without + /// allocating. + pub fn empty() -> Self { + Self::empty_in(Global) + } + /// Creates a RawVec (on the system heap) with exactly the /// capacity and alignment requirements for a `[T; cap]`. This is /// equivalent to calling RawVec::new when `cap` is 0 or T is diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index b184404c15b..757606607bb 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -324,7 +324,7 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> Vec { Vec { - buf: RawVec::new(), + buf: RawVec::empty(), len: 0, } } diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 74bb264cc67..b612a278a34 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2551,10 +2551,9 @@ impl Unique { /// This is useful for initializing types which lazily allocate, like /// `Vec::new` does. // FIXME: rename to dangling() to match NonNull? - pub fn empty() -> Self { + pub const fn empty() -> Self { unsafe { - let ptr = mem::align_of::() as *mut T; - Unique::new_unchecked(ptr) + Unique::new_unchecked(mem::align_of::() as *mut T) } } } diff --git a/src/test/run-pass/vec-const-new.rs b/src/test/run-pass/vec-const-new.rs new file mode 100644 index 00000000000..02d8cfdcf98 --- /dev/null +++ b/src/test/run-pass/vec-const-new.rs @@ -0,0 +1,15 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that Vec::new() can be used for constants + +const MY_VEC: Vec = Vec::new(); + +pub fn main() {} -- cgit 1.4.1-3-g733a5 From a2105b8e21a71f513e6840ec0571cad5c1a36012 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Wed, 25 Apr 2018 16:42:57 -0500 Subject: make RawVec::empty const --- src/liballoc/raw_vec.rs | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index dc8ad9ee061..fe18979fb51 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -54,6 +54,7 @@ pub struct RawVec { } impl RawVec { + // FIXME: this should be made `const` when `if` statements are allowed /// Like `new` but parameterized over the choice of allocator for /// the returned RawVec. pub fn new_in(a: A) -> Self { @@ -68,6 +69,7 @@ impl RawVec { } } + // FIXME: this should removed when `new_in` can be made `const` /// Like `empty` but parametrized over the choice of allocator for the returned `RawVec`. pub const fn empty_in(a: A) -> Self { // Unique::empty() doubles as "unallocated" and "zero-sized allocation" @@ -134,9 +136,10 @@ impl RawVec { Self::new_in(Global) } + // FIXME: this should removed when `new` can be made `const` /// Create a `RawVec` with capcity 0 (on the system heap), regardless of `T`, without /// allocating. - pub fn empty() -> Self { + pub const fn empty() -> Self { Self::empty_in(Global) } -- cgit 1.4.1-3-g733a5 From 20ef0e001a1620436073d01f43a4a462d1967902 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 26 Apr 2018 12:46:28 -0500 Subject: make Vec::new const :P --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 757606607bb..1d95f76fd77 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -322,7 +322,7 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Vec { + pub const fn new() -> Vec { Vec { buf: RawVec::empty(), len: 0, -- cgit 1.4.1-3-g733a5 From c122b3a42c5dd57682be353cb9433241f67ab9cb Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Thu, 26 Apr 2018 22:38:39 -0500 Subject: not insta-stable --- src/liballoc/lib.rs | 1 + src/liballoc/vec.rs | 1 + 2 files changed, 2 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 6399be98cd5..c4950786265 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -123,6 +123,7 @@ #![feature(pointer_methods)] #![feature(inclusive_range_fields)] #![cfg_attr(stage0, feature(generic_param_attrs))] +#![feature(rustc_const_unstable)] #![cfg_attr(not(test), feature(fn_traits, i128))] #![cfg_attr(test, feature(test))] diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 1d95f76fd77..415d75664ff 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -322,6 +322,7 @@ impl Vec { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_const_unstable(feature = "const_vec_new")] pub const fn new() -> Vec { Vec { buf: RawVec::empty(), -- cgit 1.4.1-3-g733a5 From f3e858aae761b30a56e8b03f510f360edeb3a2f1 Mon Sep 17 00:00:00 2001 From: George Burton Date: Fri, 27 Apr 2018 20:46:06 +0100 Subject: Update the stable attributes to use the current nightly version number --- src/liballoc/string.rs | 2 +- src/liballoc/vec.rs | 2 +- src/libstd/ffi/c_str.rs | 8 ++++---- src/libstd/ffi/os_str.rs | 8 ++++---- src/libstd/path.rs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 5f684361fdc..61a207fc5b3 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2239,7 +2239,7 @@ impl<'a> From for Cow<'a, str> { } } -#[stable(feature = "cow_from_string_ref", since = "1.28.0")] +#[stable(feature = "cow_from_string_ref", since = "1.27.0")] impl<'a> From<&'a String> for Cow<'a, str> { #[inline] fn from(s: &'a String) -> Cow<'a, str> { diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index be4c80c85d9..315a8a0aad0 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2285,7 +2285,7 @@ impl<'a, T: Clone> From> for Cow<'a, [T]> { } } -#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] +#[stable(feature = "cow_from_vec_ref", since = "1.27.0")] impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { fn from(v: &'a Vec) -> Cow<'a, [T]> { Cow::Borrowed(v.as_slice()) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 7f38cadfb22..10f59b0a3cc 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -682,7 +682,7 @@ impl Borrow for CString { fn borrow(&self) -> &CStr { self } } -#[stable(feature = "cstring_from_cow_cstr", since = "1.28.0")] +#[stable(feature = "cstring_from_cow_cstr", since = "1.27.0")] impl<'a> From> for CString { #[inline] fn from(s: Cow<'a, CStr>) -> Self { @@ -714,7 +714,7 @@ impl From for Box { } } -#[stable(feature = "cow_from_cstr", since = "1.28.0")] +#[stable(feature = "cow_from_cstr", since = "1.27.0")] impl<'a> From for Cow<'a, CStr> { #[inline] fn from(s: CString) -> Cow<'a, CStr> { @@ -722,7 +722,7 @@ impl<'a> From for Cow<'a, CStr> { } } -#[stable(feature = "cow_from_cstr", since = "1.28.0")] +#[stable(feature = "cow_from_cstr", since = "1.27.0")] impl<'a> From<&'a CStr> for Cow<'a, CStr> { #[inline] fn from(s: &'a CStr) -> Cow<'a, CStr> { @@ -730,7 +730,7 @@ impl<'a> From<&'a CStr> for Cow<'a, CStr> { } } -#[stable(feature = "cow_from_cstr", since = "1.28.0")] +#[stable(feature = "cow_from_cstr", since = "1.27.0")] impl<'a> From<&'a CString> for Cow<'a, CStr> { #[inline] fn from(s: &'a CString) -> Cow<'a, CStr> { diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index 0a3148029d0..d865ffa8e2f 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -664,7 +664,7 @@ impl<'a> From<&'a OsStr> for Rc { } } -#[stable(feature = "cow_from_osstr", since = "1.28.0")] +#[stable(feature = "cow_from_osstr", since = "1.27.0")] impl<'a> From for Cow<'a, OsStr> { #[inline] fn from(s: OsString) -> Cow<'a, OsStr> { @@ -672,7 +672,7 @@ impl<'a> From for Cow<'a, OsStr> { } } -#[stable(feature = "cow_from_osstr", since = "1.28.0")] +#[stable(feature = "cow_from_osstr", since = "1.27.0")] impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { #[inline] fn from(s: &'a OsStr) -> Cow<'a, OsStr> { @@ -680,7 +680,7 @@ impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { } } -#[stable(feature = "cow_from_osstr", since = "1.28.0")] +#[stable(feature = "cow_from_osstr", since = "1.27.0")] impl<'a> From<&'a OsString> for Cow<'a, OsStr> { #[inline] fn from(s: &'a OsString) -> Cow<'a, OsStr> { @@ -688,7 +688,7 @@ impl<'a> From<&'a OsString> for Cow<'a, OsStr> { } } -#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] +#[stable(feature = "osstring_from_cow_osstr", since = "1.27.0")] impl<'a> From> for OsString { #[inline] fn from(s: Cow<'a, OsStr>) -> Self { diff --git a/src/libstd/path.rs b/src/libstd/path.rs index b7ab14b29ca..83633210ff2 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1532,7 +1532,7 @@ impl<'a> From for Cow<'a, Path> { } } -#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] +#[stable(feature = "cow_from_pathbuf_ref", since = "1.27.0")] impl<'a> From<&'a PathBuf> for Cow<'a, Path> { #[inline] fn from(p: &'a PathBuf) -> Cow<'a, Path> { @@ -1540,7 +1540,7 @@ impl<'a> From<&'a PathBuf> for Cow<'a, Path> { } } -#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")] +#[stable(feature = "pathbuf_from_cow_path", since = "1.27.0")] impl<'a> From> for PathBuf { #[inline] fn from(p: Cow<'a, Path>) -> Self { -- cgit 1.4.1-3-g733a5 From 3dbdccc6a9c1ead58325d415381b25c676386c34 Mon Sep 17 00:00:00 2001 From: "Zack M. Davis" Date: Sat, 10 Mar 2018 16:23:28 -0800 Subject: stabilize `#[must_use]` for functions and must-use operators This is in the matter of RFC 1940 and tracking issue #43302. --- .../src/language-features/fn-must-use.md | 30 ---- src/liballoc/lib.rs | 2 +- src/liballoc/tests/slice.rs | 1 + src/libcore/lib.rs | 2 +- src/librustc_lint/unused.rs | 104 +++++++------- src/libstd/ffi/c_str.rs | 2 + src/libstd/sync/mpsc/select.rs | 2 + src/libsyntax/feature_gate.rs | 22 +-- .../ui/feature-gate-fn_must_use-cap-lints-allow.rs | 22 --- ...feature-gate-fn_must_use-cap-lints-allow.stderr | 8 -- src/test/ui/feature-gate-fn_must_use.rs | 31 ----- src/test/ui/feature-gate-fn_must_use.stderr | 24 ---- .../issue-43106-gating-of-builtin-attrs.rs | 1 - .../issue-43106-gating-of-builtin-attrs.stderr | 154 ++++++++++----------- src/test/ui/fn_must_use.rs | 78 +++++++++++ src/test/ui/fn_must_use.stderr | 48 +++++++ src/test/ui/lint/must-use-ops.rs | 1 - src/test/ui/lint/must-use-ops.stderr | 44 +++--- .../rfc_1940-must_use_on_functions/fn_must_use.rs | 79 ----------- .../fn_must_use.stderr | 48 ------- src/test/ui/span/gated-features-attr-spans.rs | 23 --- src/test/ui/span/gated-features-attr-spans.stderr | 18 +-- 22 files changed, 284 insertions(+), 460 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/fn-must-use.md delete mode 100644 src/test/ui/feature-gate-fn_must_use-cap-lints-allow.rs delete mode 100644 src/test/ui/feature-gate-fn_must_use-cap-lints-allow.stderr delete mode 100644 src/test/ui/feature-gate-fn_must_use.rs delete mode 100644 src/test/ui/feature-gate-fn_must_use.stderr create mode 100644 src/test/ui/fn_must_use.rs create mode 100644 src/test/ui/fn_must_use.stderr delete mode 100644 src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs delete mode 100644 src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/fn-must-use.md b/src/doc/unstable-book/src/language-features/fn-must-use.md deleted file mode 100644 index 71b6cd663a0..00000000000 --- a/src/doc/unstable-book/src/language-features/fn-must-use.md +++ /dev/null @@ -1,30 +0,0 @@ -# `fn_must_use` - -The tracking issue for this feature is [#43302]. - -[#43302]: https://github.com/rust-lang/rust/issues/43302 - ------------------------- - -The `fn_must_use` feature allows functions and methods to be annotated with -`#[must_use]`, indicating that the `unused_must_use` lint should require their -return values to be used (similarly to how types annotated with `must_use`, -most notably `Result`, are linted if not used). - -## Examples - -```rust -#![feature(fn_must_use)] - -#[must_use] -fn double(x: i32) -> i32 { - 2 * x -} - -fn main() { - double(4); // warning: unused return value of `double` which must be used - - let _ = double(4); // (no warning) -} - -``` diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 021395d0c82..fa74352c23c 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -96,7 +96,7 @@ #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] -#![feature(fn_must_use)] +#![cfg_attr(stage0, feature(fn_must_use))] #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 99d9c51efc7..6fd0b33f02a 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -1282,6 +1282,7 @@ fn test_box_slice_clone() { } #[test] +#[allow(unused_must_use)] // here, we care about the side effects of `.clone()` #[cfg_attr(target_os = "emscripten", ignore)] fn test_box_slice_clone_panics() { use std::sync::Arc; diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 0e21a3327fd..f4ed24cc3a3 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -76,7 +76,6 @@ #![feature(doc_cfg)] #![feature(doc_spotlight)] #![feature(extern_types)] -#![feature(fn_must_use)] #![feature(fundamental)] #![feature(intrinsics)] #![feature(iterator_flatten)] @@ -114,6 +113,7 @@ #![cfg_attr(stage0, feature(target_feature))] #![cfg_attr(stage0, feature(cfg_target_feature))] +#![cfg_attr(stage0, feature(fn_must_use))] #[prelude_import] #[allow(unused)] diff --git a/src/librustc_lint/unused.rs b/src/librustc_lint/unused.rs index c32e9cdce0e..9e1b75ba336 100644 --- a/src/librustc_lint/unused.rs +++ b/src/librustc_lint/unused.rs @@ -73,59 +73,59 @@ impl<'a, 'tcx> LateLintPass<'a, 'tcx> for UnusedResults { let mut fn_warned = false; let mut op_warned = false; - if cx.tcx.features().fn_must_use { - let maybe_def = match expr.node { - hir::ExprCall(ref callee, _) => { - match callee.node { - hir::ExprPath(ref qpath) => { - let def = cx.tables.qpath_def(qpath, callee.hir_id); - if let Def::Fn(_) = def { - Some(def) - } else { // `Def::Local` if it was a closure, for which we - None // do not currently support must-use linting - } - }, - _ => None - } - }, - hir::ExprMethodCall(..) => { - cx.tables.type_dependent_defs().get(expr.hir_id).cloned() - }, - _ => None - }; - if let Some(def) = maybe_def { - let def_id = def.def_id(); - fn_warned = check_must_use(cx, def_id, s.span, "return value of "); - } - let must_use_op = match expr.node { - // Hardcoding operators here seemed more expedient than the - // refactoring that would be needed to look up the `#[must_use]` - // attribute which does exist on the comparison trait methods - hir::ExprBinary(bin_op, ..) => { - match bin_op.node { - hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => { - Some("comparison") - }, - hir::BiAdd | hir::BiSub | hir::BiDiv | hir::BiMul | hir::BiRem => { - Some("arithmetic operation") - }, - hir::BiAnd | hir::BiOr => { - Some("logical operation") - }, - hir::BiBitXor | hir::BiBitAnd | hir::BiBitOr | hir::BiShl | hir::BiShr => { - Some("bitwise operation") - }, - } - }, - hir::ExprUnary(..) => Some("unary operation"), - _ => None - }; - if let Some(must_use_op) = must_use_op { - cx.span_lint(UNUSED_MUST_USE, expr.span, - &format!("unused {} which must be used", must_use_op)); - op_warned = true; - } + let maybe_def = match expr.node { + hir::ExprCall(ref callee, _) => { + match callee.node { + hir::ExprPath(ref qpath) => { + let def = cx.tables.qpath_def(qpath, callee.hir_id); + if let Def::Fn(_) = def { + Some(def) + } else { // `Def::Local` if it was a closure, for which we + None // do not currently support must-use linting + } + }, + _ => None + } + }, + hir::ExprMethodCall(..) => { + cx.tables.type_dependent_defs().get(expr.hir_id).cloned() + }, + _ => None + }; + if let Some(def) = maybe_def { + let def_id = def.def_id(); + fn_warned = check_must_use(cx, def_id, s.span, "return value of "); } + let must_use_op = match expr.node { + // Hardcoding operators here seemed more expedient than the + // refactoring that would be needed to look up the `#[must_use]` + // attribute which does exist on the comparison trait methods + hir::ExprBinary(bin_op, ..) => { + match bin_op.node { + hir::BiEq | hir::BiLt | hir::BiLe | hir::BiNe | hir::BiGe | hir::BiGt => { + Some("comparison") + }, + hir::BiAdd | hir::BiSub | hir::BiDiv | hir::BiMul | hir::BiRem => { + Some("arithmetic operation") + }, + hir::BiAnd | hir::BiOr => { + Some("logical operation") + }, + hir::BiBitXor | hir::BiBitAnd | hir::BiBitOr | hir::BiShl | hir::BiShr => { + Some("bitwise operation") + }, + } + }, + hir::ExprUnary(..) => Some("unary operation"), + _ => None + }; + + if let Some(must_use_op) = must_use_op { + cx.span_lint(UNUSED_MUST_USE, expr.span, + &format!("unused {} which must be used", must_use_op)); + op_warned = true; + } + if !(ty_warned || fn_warned || op_warned) { cx.span_lint(UNUSED_RESULTS, s.span, "unused result"); } diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index c88c2bc9137..d4937c00012 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -988,6 +988,7 @@ impl CStr { /// behavior when `ptr` is used inside the `unsafe` block: /// /// ```no_run + /// # #![allow(unused_must_use)] /// use std::ffi::{CString}; /// /// let ptr = CString::new("Hello").unwrap().as_ptr(); @@ -1003,6 +1004,7 @@ impl CStr { /// To fix the problem, bind the `CString` to a local variable: /// /// ```no_run + /// # #![allow(unused_must_use)] /// use std::ffi::{CString}; /// /// let hello = CString::new("Hello").unwrap(); diff --git a/src/libstd/sync/mpsc/select.rs b/src/libstd/sync/mpsc/select.rs index a9f3cea243f..9310dad9172 100644 --- a/src/libstd/sync/mpsc/select.rs +++ b/src/libstd/sync/mpsc/select.rs @@ -518,6 +518,7 @@ mod tests { } } + #[allow(unused_must_use)] #[test] fn cloning() { let (tx1, rx1) = channel::(); @@ -540,6 +541,7 @@ mod tests { tx3.send(()).unwrap(); } + #[allow(unused_must_use)] #[test] fn cloning2() { let (tx1, rx1) = channel::(); diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index a4a83712a08..f16b1ba440a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -369,9 +369,6 @@ declare_features! ( // #[doc(include="some-file")] (active, external_doc, "1.22.0", Some(44732), None), - // allow `#[must_use]` on functions and comparison operators (RFC 1940) - (active, fn_must_use, "1.21.0", Some(43302), None), - // Future-proofing enums/structs with #[non_exhaustive] attribute (RFC 2008) (active, non_exhaustive, "1.22.0", Some(44109), None), @@ -591,6 +588,8 @@ declare_features! ( (accepted, target_feature, "1.27.0", None, None), // Trait object syntax with `dyn` prefix (accepted, dyn_trait, "1.27.0", Some(44662), None), + // allow `#[must_use]` on functions; and, must-use operators (RFC 1940) + (accepted, fn_must_use, "1.27.0", Some(43302), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1545,11 +1544,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { function may change over time, for now \ a top-level `fn main()` is required"); } - if let Some(attr) = attr::find_by_name(&i.attrs[..], "must_use") { - gate_feature_post!(&self, fn_must_use, attr.span, - "`#[must_use]` on functions is experimental", - GateStrength::Soft); - } } ast::ItemKind::Struct(..) => { @@ -1581,7 +1575,7 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { "trait aliases are not yet fully implemented"); } - ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, ref impl_items) => { + ast::ItemKind::Impl(_, polarity, defaultness, _, _, _, _) => { if polarity == ast::ImplPolarity::Negative { gate_feature_post!(&self, optin_builtin_traits, i.span, @@ -1594,16 +1588,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { i.span, "specialization is unstable"); } - - for impl_item in impl_items { - if let ast::ImplItemKind::Method(..) = impl_item.node { - if let Some(attr) = attr::find_by_name(&impl_item.attrs[..], "must_use") { - gate_feature_post!(&self, fn_must_use, attr.span, - "`#[must_use]` on methods is experimental", - GateStrength::Soft); - } - } - } } ast::ItemKind::Trait(ast::IsAuto::Yes, ..) => { diff --git a/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.rs b/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.rs deleted file mode 100644 index 1c04199c05f..00000000000 --- a/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.rs +++ /dev/null @@ -1,22 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: --cap-lints allow - -// This tests that the fn_must_use feature-gate warning respects the lint -// cap. (See discussion in Issue #44213.) - -#![feature(rustc_attrs)] - -#[must_use] // (no feature-gate warning because of the lint cap!) -fn need_to_use_it() -> bool { true } - -#[rustc_error] -fn main() {} //~ ERROR compilation successful diff --git a/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.stderr b/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.stderr deleted file mode 100644 index a2c1dedff38..00000000000 --- a/src/test/ui/feature-gate-fn_must_use-cap-lints-allow.stderr +++ /dev/null @@ -1,8 +0,0 @@ -error: compilation successful - --> $DIR/feature-gate-fn_must_use-cap-lints-allow.rs:22:1 - | -LL | fn main() {} //~ ERROR compilation successful - | ^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/feature-gate-fn_must_use.rs b/src/test/ui/feature-gate-fn_must_use.rs deleted file mode 100644 index 72fdcc76cf4..00000000000 --- a/src/test/ui/feature-gate-fn_must_use.rs +++ /dev/null @@ -1,31 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(rustc_attrs)] - -struct MyStruct; - -impl MyStruct { - #[must_use] //~ WARN `#[must_use]` on methods is experimental - fn need_to_use_method() -> bool { true } -} - -#[must_use] //~ WARN `#[must_use]` on functions is experimental -fn need_to_use_it() -> bool { true } - - -// Feature gates are tidy-required to have a specially named (or -// comment-annotated) compile-fail test (which MUST fail), but for -// backwards-compatibility reasons, we want `#[must_use]` on functions to be -// compilable even if the `fn_must_use` feature is absent, thus necessitating -// the usage of `#[rustc_error]` here, pragmatically if awkwardly solving this -// dilemma until a superior solution can be devised. -#[rustc_error] -fn main() {} //~ ERROR compilation successful diff --git a/src/test/ui/feature-gate-fn_must_use.stderr b/src/test/ui/feature-gate-fn_must_use.stderr deleted file mode 100644 index 431c57abd26..00000000000 --- a/src/test/ui/feature-gate-fn_must_use.stderr +++ /dev/null @@ -1,24 +0,0 @@ -warning: `#[must_use]` on methods is experimental (see issue #43302) - --> $DIR/feature-gate-fn_must_use.rs:16:5 - | -LL | #[must_use] //~ WARN `#[must_use]` on methods is experimental - | ^^^^^^^^^^^ - | - = help: add #![feature(fn_must_use)] to the crate attributes to enable - -warning: `#[must_use]` on functions is experimental (see issue #43302) - --> $DIR/feature-gate-fn_must_use.rs:20:1 - | -LL | #[must_use] //~ WARN `#[must_use]` on functions is experimental - | ^^^^^^^^^^^ - | - = help: add #![feature(fn_must_use)] to the crate attributes to enable - -error: compilation successful - --> $DIR/feature-gate-fn_must_use.rs:31:1 - | -LL | fn main() {} //~ ERROR compilation successful - | ^^^^^^^^^^^^ - -error: aborting due to previous error - diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs index 21950402c8c..7b0c81dbab6 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.rs @@ -661,7 +661,6 @@ mod must_use { mod inner { #![must_use="1400"] } #[must_use = "1400"] fn f() { } - //~^ WARN `#[must_use]` on functions is experimental #[must_use = "1400"] struct S; diff --git a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr index 0beed627987..76ab50c9089 100644 --- a/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr +++ b/src/test/ui/feature-gate/issue-43106-gating-of-builtin-attrs.stderr @@ -12,14 +12,6 @@ LL | mod inner { #![macro_escape] } | = help: consider an outer attribute, #[macro_use] mod ... -warning: `#[must_use]` on functions is experimental (see issue #43302) - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:663:5 - | -LL | #[must_use = "1400"] fn f() { } - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(fn_must_use)] to the crate attributes to enable - warning: unknown lint: `x5400` --> $DIR/issue-43106-gating-of-builtin-attrs.rs:49:33 | @@ -799,433 +791,433 @@ LL | #[no_std = "2600"] | ^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:692:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:691:17 | LL | mod inner { #![crate_name="0900"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:696:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:695:5 | LL | #[crate_name = "0900"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:700:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:699:5 | LL | #[crate_name = "0900"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:704:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:703:5 | LL | #[crate_name = "0900"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:707:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:708:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:707:5 | LL | #[crate_name = "0900"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:688:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:687:1 | LL | #[crate_name = "0900"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:717:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:716:17 | LL | mod inner { #![crate_type="0800"] } | ^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:721:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:720:5 | LL | #[crate_type = "0800"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:725:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:724:5 | LL | #[crate_type = "0800"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:729:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:728:5 | LL | #[crate_type = "0800"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:733:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:732:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:733:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:732:5 | LL | #[crate_type = "0800"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:713:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:712:1 | LL | #[crate_type = "0800"] | ^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:742:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:742:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:741:17 | LL | mod inner { #![feature(x0600)] } | ^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:746:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:745:5 | LL | #[feature(x0600)] fn f() { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:749:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:750:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:749:5 | LL | #[feature(x0600)] struct S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:754:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:753:5 | LL | #[feature(x0600)] type T = S; | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:757:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:758:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:757:5 | LL | #[feature(x0600)] impl S { } | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:738:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:738:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:737:1 | LL | #[feature(x0600)] | ^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:768:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:767:17 | LL | mod inner { #![no_main="0400"] } | ^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:768:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:767:17 | LL | mod inner { #![no_main="0400"] } | ^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:771:5 | LL | #[no_main = "0400"] fn f() { } | ^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:772:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:771:5 | LL | #[no_main = "0400"] fn f() { } | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:5 | LL | #[no_main = "0400"] struct S; | ^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:776:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:775:5 | LL | #[no_main = "0400"] struct S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:779:5 | LL | #[no_main = "0400"] type T = S; | ^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:780:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:779:5 | LL | #[no_main = "0400"] type T = S; | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 | LL | #[no_main = "0400"] impl S { } | ^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:784:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:783:5 | LL | #[no_main = "0400"] impl S { } | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:763:1 | LL | #[no_main = "0400"] | ^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:764:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:763:1 | LL | #[no_main = "0400"] | ^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:806:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:806:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:805:17 | LL | mod inner { #![recursion_limit="0200"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:810:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:810:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:809:5 | LL | #[recursion_limit="0200"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:813:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:814:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:813:5 | LL | #[recursion_limit="0200"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:818:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:817:5 | LL | #[recursion_limit="0200"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:821:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:822:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:821:5 | LL | #[recursion_limit="0200"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:802:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:802:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:801:1 | LL | #[recursion_limit="0200"] | ^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:831:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be in the root module - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:831:17 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:830:17 | LL | mod inner { #![type_length_limit="0100"] } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:835:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:835:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:834:5 | LL | #[type_length_limit="0100"] fn f() { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:838:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:839:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:838:5 | LL | #[type_length_limit="0100"] struct S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:842:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:843:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:842:5 | LL | #[type_length_limit="0100"] type T = S; | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:847:5 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:846:5 | LL | #[type_length_limit="0100"] impl S { } | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: unused attribute - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ warning: crate-level attribute should be an inner attribute: add an exclamation mark: #![foo] - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:827:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:826:1 | LL | #[type_length_limit="0100"] | ^^^^^^^^^^^^^^^^^^^^^^^^^^^ @@ -1309,7 +1301,7 @@ LL | #![proc_macro_derive = "2500"] //~ WARN unused attribute | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ error: compilation successful - --> $DIR/issue-43106-gating-of-builtin-attrs.rs:858:1 + --> $DIR/issue-43106-gating-of-builtin-attrs.rs:857:1 | LL | / fn main() { //~ ERROR compilation successful LL | | println!("Hello World"); diff --git a/src/test/ui/fn_must_use.rs b/src/test/ui/fn_must_use.rs new file mode 100644 index 00000000000..def23046db2 --- /dev/null +++ b/src/test/ui/fn_must_use.rs @@ -0,0 +1,78 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-pass + +#![warn(unused_must_use)] + +#[derive(PartialEq, Eq)] +struct MyStruct { + n: usize, +} + +impl MyStruct { + #[must_use] + fn need_to_use_this_method_value(&self) -> usize { + self.n + } +} + +trait EvenNature { + #[must_use = "no side effects"] + fn is_even(&self) -> bool; +} + +impl EvenNature for MyStruct { + fn is_even(&self) -> bool { + self.n % 2 == 0 + } +} + +trait Replaceable { + fn replace(&mut self, substitute: usize) -> usize; +} + +impl Replaceable for MyStruct { + // ↓ N.b.: `#[must_use]` attribute on a particular trait implementation + // method won't work; the attribute should be on the method signature in + // the trait's definition. + #[must_use] + fn replace(&mut self, substitute: usize) -> usize { + let previously = self.n; + self.n = substitute; + previously + } +} + +#[must_use = "it's important"] +fn need_to_use_this_value() -> bool { + false +} + +fn main() { + need_to_use_this_value(); //~ WARN unused return value + + let mut m = MyStruct { n: 2 }; + let n = MyStruct { n: 3 }; + + m.need_to_use_this_method_value(); //~ WARN unused return value + m.is_even(); // trait method! + //~^ WARN unused return value + + m.replace(3); // won't warn (annotation needs to be in trait definition) + + // comparison methods are `must_use` + 2.eq(&3); //~ WARN unused return value + m.eq(&n); //~ WARN unused return value + + // lint includes comparison operators + 2 == 3; //~ WARN unused comparison + m == n; //~ WARN unused comparison +} diff --git a/src/test/ui/fn_must_use.stderr b/src/test/ui/fn_must_use.stderr new file mode 100644 index 00000000000..5026dac0a94 --- /dev/null +++ b/src/test/ui/fn_must_use.stderr @@ -0,0 +1,48 @@ +warning: unused return value of `need_to_use_this_value` which must be used: it's important + --> $DIR/fn_must_use.rs:60:5 + | +LL | need_to_use_this_value(); //~ WARN unused return value + | ^^^^^^^^^^^^^^^^^^^^^^^^^ + | +note: lint level defined here + --> $DIR/fn_must_use.rs:13:9 + | +LL | #![warn(unused_must_use)] + | ^^^^^^^^^^^^^^^ + +warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used + --> $DIR/fn_must_use.rs:65:5 + | +LL | m.need_to_use_this_method_value(); //~ WARN unused return value + | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +warning: unused return value of `EvenNature::is_even` which must be used: no side effects + --> $DIR/fn_must_use.rs:66:5 + | +LL | m.is_even(); // trait method! + | ^^^^^^^^^^^^ + +warning: unused return value of `std::cmp::PartialEq::eq` which must be used + --> $DIR/fn_must_use.rs:72:5 + | +LL | 2.eq(&3); //~ WARN unused return value + | ^^^^^^^^^ + +warning: unused return value of `std::cmp::PartialEq::eq` which must be used + --> $DIR/fn_must_use.rs:73:5 + | +LL | m.eq(&n); //~ WARN unused return value + | ^^^^^^^^^ + +warning: unused comparison which must be used + --> $DIR/fn_must_use.rs:76:5 + | +LL | 2 == 3; //~ WARN unused comparison + | ^^^^^^ + +warning: unused comparison which must be used + --> $DIR/fn_must_use.rs:77:5 + | +LL | m == n; //~ WARN unused comparison + | ^^^^^^ + diff --git a/src/test/ui/lint/must-use-ops.rs b/src/test/ui/lint/must-use-ops.rs index 4ed82ab3b40..c0575f817c8 100644 --- a/src/test/ui/lint/must-use-ops.rs +++ b/src/test/ui/lint/must-use-ops.rs @@ -12,7 +12,6 @@ // compile-pass -#![feature(fn_must_use)] #![warn(unused_must_use)] fn main() { diff --git a/src/test/ui/lint/must-use-ops.stderr b/src/test/ui/lint/must-use-ops.stderr index f444ef09075..5703536ef48 100644 --- a/src/test/ui/lint/must-use-ops.stderr +++ b/src/test/ui/lint/must-use-ops.stderr @@ -1,131 +1,131 @@ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:23:5 + --> $DIR/must-use-ops.rs:22:5 | LL | val == 1; | ^^^^^^^^ | note: lint level defined here - --> $DIR/must-use-ops.rs:16:9 + --> $DIR/must-use-ops.rs:15:9 | LL | #![warn(unused_must_use)] | ^^^^^^^^^^^^^^^ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:24:5 + --> $DIR/must-use-ops.rs:23:5 | LL | val < 1; | ^^^^^^^ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:25:5 + --> $DIR/must-use-ops.rs:24:5 | LL | val <= 1; | ^^^^^^^^ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:26:5 + --> $DIR/must-use-ops.rs:25:5 | LL | val != 1; | ^^^^^^^^ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:27:5 + --> $DIR/must-use-ops.rs:26:5 | LL | val >= 1; | ^^^^^^^^ warning: unused comparison which must be used - --> $DIR/must-use-ops.rs:28:5 + --> $DIR/must-use-ops.rs:27:5 | LL | val > 1; | ^^^^^^^ warning: unused arithmetic operation which must be used - --> $DIR/must-use-ops.rs:31:5 + --> $DIR/must-use-ops.rs:30:5 | LL | val + 2; | ^^^^^^^ warning: unused arithmetic operation which must be used - --> $DIR/must-use-ops.rs:32:5 + --> $DIR/must-use-ops.rs:31:5 | LL | val - 2; | ^^^^^^^ warning: unused arithmetic operation which must be used - --> $DIR/must-use-ops.rs:33:5 + --> $DIR/must-use-ops.rs:32:5 | LL | val / 2; | ^^^^^^^ warning: unused arithmetic operation which must be used - --> $DIR/must-use-ops.rs:34:5 + --> $DIR/must-use-ops.rs:33:5 | LL | val * 2; | ^^^^^^^ warning: unused arithmetic operation which must be used - --> $DIR/must-use-ops.rs:35:5 + --> $DIR/must-use-ops.rs:34:5 | LL | val % 2; | ^^^^^^^ warning: unused logical operation which must be used - --> $DIR/must-use-ops.rs:38:5 + --> $DIR/must-use-ops.rs:37:5 | LL | true && true; | ^^^^^^^^^^^^ warning: unused logical operation which must be used - --> $DIR/must-use-ops.rs:39:5 + --> $DIR/must-use-ops.rs:38:5 | LL | false || true; | ^^^^^^^^^^^^^ warning: unused bitwise operation which must be used - --> $DIR/must-use-ops.rs:42:5 + --> $DIR/must-use-ops.rs:41:5 | LL | 5 ^ val; | ^^^^^^^ warning: unused bitwise operation which must be used - --> $DIR/must-use-ops.rs:43:5 + --> $DIR/must-use-ops.rs:42:5 | LL | 5 & val; | ^^^^^^^ warning: unused bitwise operation which must be used - --> $DIR/must-use-ops.rs:44:5 + --> $DIR/must-use-ops.rs:43:5 | LL | 5 | val; | ^^^^^^^ warning: unused bitwise operation which must be used - --> $DIR/must-use-ops.rs:45:5 + --> $DIR/must-use-ops.rs:44:5 | LL | 5 << val; | ^^^^^^^^ warning: unused bitwise operation which must be used - --> $DIR/must-use-ops.rs:46:5 + --> $DIR/must-use-ops.rs:45:5 | LL | 5 >> val; | ^^^^^^^^ warning: unused unary operation which must be used - --> $DIR/must-use-ops.rs:49:5 + --> $DIR/must-use-ops.rs:48:5 | LL | !val; | ^^^^ warning: unused unary operation which must be used - --> $DIR/must-use-ops.rs:50:5 + --> $DIR/must-use-ops.rs:49:5 | LL | -val; | ^^^^ warning: unused unary operation which must be used - --> $DIR/must-use-ops.rs:51:5 + --> $DIR/must-use-ops.rs:50:5 | LL | *val_pointer; | ^^^^^^^^^^^^ diff --git a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs deleted file mode 100644 index d20ebf0b740..00000000000 --- a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.rs +++ /dev/null @@ -1,79 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-pass - -#![feature(fn_must_use)] -#![warn(unused_must_use)] - -#[derive(PartialEq, Eq)] -struct MyStruct { - n: usize, -} - -impl MyStruct { - #[must_use] - fn need_to_use_this_method_value(&self) -> usize { - self.n - } -} - -trait EvenNature { - #[must_use = "no side effects"] - fn is_even(&self) -> bool; -} - -impl EvenNature for MyStruct { - fn is_even(&self) -> bool { - self.n % 2 == 0 - } -} - -trait Replaceable { - fn replace(&mut self, substitute: usize) -> usize; -} - -impl Replaceable for MyStruct { - // ↓ N.b.: `#[must_use]` attribute on a particular trait implementation - // method won't work; the attribute should be on the method signature in - // the trait's definition. - #[must_use] - fn replace(&mut self, substitute: usize) -> usize { - let previously = self.n; - self.n = substitute; - previously - } -} - -#[must_use = "it's important"] -fn need_to_use_this_value() -> bool { - false -} - -fn main() { - need_to_use_this_value(); //~ WARN unused return value - - let mut m = MyStruct { n: 2 }; - let n = MyStruct { n: 3 }; - - m.need_to_use_this_method_value(); //~ WARN unused return value - m.is_even(); // trait method! - //~^ WARN unused return value - - m.replace(3); // won't warn (annotation needs to be in trait definition) - - // comparison methods are `must_use` - 2.eq(&3); //~ WARN unused return value - m.eq(&n); //~ WARN unused return value - - // lint includes comparison operators - 2 == 3; //~ WARN unused comparison - m == n; //~ WARN unused comparison -} diff --git a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr b/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr deleted file mode 100644 index d0a8bb525b6..00000000000 --- a/src/test/ui/rfc_1940-must_use_on_functions/fn_must_use.stderr +++ /dev/null @@ -1,48 +0,0 @@ -warning: unused return value of `need_to_use_this_value` which must be used: it's important - --> $DIR/fn_must_use.rs:61:5 - | -LL | need_to_use_this_value(); //~ WARN unused return value - | ^^^^^^^^^^^^^^^^^^^^^^^^^ - | -note: lint level defined here - --> $DIR/fn_must_use.rs:14:9 - | -LL | #![warn(unused_must_use)] - | ^^^^^^^^^^^^^^^ - -warning: unused return value of `MyStruct::need_to_use_this_method_value` which must be used - --> $DIR/fn_must_use.rs:66:5 - | -LL | m.need_to_use_this_method_value(); //~ WARN unused return value - | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -warning: unused return value of `EvenNature::is_even` which must be used: no side effects - --> $DIR/fn_must_use.rs:67:5 - | -LL | m.is_even(); // trait method! - | ^^^^^^^^^^^^ - -warning: unused return value of `std::cmp::PartialEq::eq` which must be used - --> $DIR/fn_must_use.rs:73:5 - | -LL | 2.eq(&3); //~ WARN unused return value - | ^^^^^^^^^ - -warning: unused return value of `std::cmp::PartialEq::eq` which must be used - --> $DIR/fn_must_use.rs:74:5 - | -LL | m.eq(&n); //~ WARN unused return value - | ^^^^^^^^^ - -warning: unused comparison which must be used - --> $DIR/fn_must_use.rs:77:5 - | -LL | 2 == 3; //~ WARN unused comparison - | ^^^^^^ - -warning: unused comparison which must be used - --> $DIR/fn_must_use.rs:78:5 - | -LL | m == n; //~ WARN unused comparison - | ^^^^^^ - diff --git a/src/test/ui/span/gated-features-attr-spans.rs b/src/test/ui/span/gated-features-attr-spans.rs index 83a4c5d5dd2..eff1f98eb71 100644 --- a/src/test/ui/span/gated-features-attr-spans.rs +++ b/src/test/ui/span/gated-features-attr-spans.rs @@ -8,33 +8,10 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(attr_literals)] - -#[repr(align(16))] -struct Gem { - mohs_hardness: u8, - poofed: bool, - weapon: Weapon, -} - #[repr(simd)] //~ ERROR are experimental struct Weapon { name: String, damage: u32 } -impl Gem { - #[must_use] fn summon_weapon(&self) -> Weapon { self.weapon } - //~^ WARN is experimental -} - -#[must_use] //~ WARN is experimental -fn bubble(gem: Gem) -> Result { - if gem.poofed { - Ok(gem) - } else { - Err(()) - } -} - fn main() {} diff --git a/src/test/ui/span/gated-features-attr-spans.stderr b/src/test/ui/span/gated-features-attr-spans.stderr index 179daf83c3c..a99530529fc 100644 --- a/src/test/ui/span/gated-features-attr-spans.stderr +++ b/src/test/ui/span/gated-features-attr-spans.stderr @@ -1,27 +1,11 @@ error[E0658]: SIMD types are experimental and possibly buggy (see issue #27731) - --> $DIR/gated-features-attr-spans.rs:20:1 + --> $DIR/gated-features-attr-spans.rs:11:1 | LL | #[repr(simd)] //~ ERROR are experimental | ^^^^^^^^^^^^^ | = help: add #![feature(repr_simd)] to the crate attributes to enable -warning: `#[must_use]` on methods is experimental (see issue #43302) - --> $DIR/gated-features-attr-spans.rs:27:5 - | -LL | #[must_use] fn summon_weapon(&self) -> Weapon { self.weapon } - | ^^^^^^^^^^^ - | - = help: add #![feature(fn_must_use)] to the crate attributes to enable - -warning: `#[must_use]` on functions is experimental (see issue #43302) - --> $DIR/gated-features-attr-spans.rs:31:1 - | -LL | #[must_use] //~ WARN is experimental - | ^^^^^^^^^^^ - | - = help: add #![feature(fn_must_use)] to the crate attributes to enable - error: aborting due to previous error For more information about this error, try `rustc --explain E0658`. -- cgit 1.4.1-3-g733a5 From e5280e452f194ea7b4066c50b7954e07cb054161 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Sun, 29 Apr 2018 17:13:49 -0500 Subject: use const trick --- src/liballoc/raw_vec.rs | 29 ++++++----------------------- src/liballoc/vec.rs | 2 +- 2 files changed, 7 insertions(+), 24 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index fe18979fb51..6ca668fda59 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -54,28 +54,18 @@ pub struct RawVec { } impl RawVec { - // FIXME: this should be made `const` when `if` statements are allowed /// Like `new` but parameterized over the choice of allocator for /// the returned RawVec. - pub fn new_in(a: A) -> Self { + pub const fn new_in(a: A) -> Self { // !0 is usize::MAX. This branch should be stripped at compile time. - let cap = if mem::size_of::() == 0 { !0 } else { 0 }; + // FIXME(mark-i-m): use this line when `if`s are allowed in `const` + //let cap = if mem::size_of::() == 0 { !0 } else { 0 }; // Unique::empty() doubles as "unallocated" and "zero-sized allocation" RawVec { ptr: Unique::empty(), - cap, - a, - } - } - - // FIXME: this should removed when `new_in` can be made `const` - /// Like `empty` but parametrized over the choice of allocator for the returned `RawVec`. - pub const fn empty_in(a: A) -> Self { - // Unique::empty() doubles as "unallocated" and "zero-sized allocation" - RawVec { - ptr: Unique::empty(), - cap: 0, + // FIXME(mark-i-m): use `cap` when ifs are allowed in const + cap: [0, !0][(mem::size_of::() != 0) as usize], a, } } @@ -132,17 +122,10 @@ impl RawVec { /// RawVec with capacity 0. If T has 0 size, then it makes a /// RawVec with capacity `usize::MAX`. Useful for implementing /// delayed allocation. - pub fn new() -> Self { + pub const fn new() -> Self { Self::new_in(Global) } - // FIXME: this should removed when `new` can be made `const` - /// Create a `RawVec` with capcity 0 (on the system heap), regardless of `T`, without - /// allocating. - pub const fn empty() -> Self { - Self::empty_in(Global) - } - /// Creates a RawVec (on the system heap) with exactly the /// capacity and alignment requirements for a `[T; cap]`. This is /// equivalent to calling RawVec::new when `cap` is 0 or T is diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 415d75664ff..35d0a69a05a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -325,7 +325,7 @@ impl Vec { #[rustc_const_unstable(feature = "const_vec_new")] pub const fn new() -> Vec { Vec { - buf: RawVec::empty(), + buf: RawVec::new(), len: 0, } } -- cgit 1.4.1-3-g733a5 From f9f992379de4a82637ec4bf717ff42f27872bc48 Mon Sep 17 00:00:00 2001 From: Mark Mansi Date: Sun, 29 Apr 2018 17:27:17 -0500 Subject: heh, logic is hard --- src/liballoc/raw_vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 6ca668fda59..eb25ae17511 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -65,7 +65,7 @@ impl RawVec { RawVec { ptr: Unique::empty(), // FIXME(mark-i-m): use `cap` when ifs are allowed in const - cap: [0, !0][(mem::size_of::() != 0) as usize], + cap: [0, !0][(mem::size_of::() == 0) as usize], a, } } -- cgit 1.4.1-3-g733a5 From 0842dc67238969e39b0a08d2c4314ceefd19caa2 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:02 -0400 Subject: collect str SliceIndex tests into a mod GitHub users: I think you can add ?w=1 to the url for a vastly cleaner whitespace-ignoring diff --- src/liballoc/tests/str.rs | 277 +++++++++++++++++++++++----------------------- 1 file changed, 140 insertions(+), 137 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index a03b61ec97e..c9536cbe168 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,113 +291,160 @@ fn test_replace_pattern() { assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ"); } -#[test] -fn test_slice() { - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); - - let data = "ประเทศไทย中华"; - assert_eq!("ป", &data[0..3]); - assert_eq!("ร", &data[3..6]); - assert_eq!("", &data[3..3]); - assert_eq!("华", &data[30..33]); - - fn a_million_letter_x() -> String { - let mut i = 0; - let mut rs = String::new(); - while i < 100000 { - rs.push_str("华华华华华华华华华华"); - i += 1; +mod slice_index { + #[test] + fn test_slice() { + assert_eq!("ab", &"abc"[0..2]); + assert_eq!("bc", &"abc"[1..3]); + assert_eq!("", &"abc"[1..1]); + assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); + + let data = "ประเทศไทย中华"; + assert_eq!("ป", &data[0..3]); + assert_eq!("ร", &data[3..6]); + assert_eq!("", &data[3..3]); + assert_eq!("华", &data[30..33]); + + fn a_million_letter_x() -> String { + let mut i = 0; + let mut rs = String::new(); + while i < 100000 { + rs.push_str("华华华华华华华华华华"); + i += 1; + } + rs } - rs - } - fn half_a_million_letter_x() -> String { - let mut i = 0; - let mut rs = String::new(); - while i < 100000 { - rs.push_str("华华华华华"); - i += 1; + fn half_a_million_letter_x() -> String { + let mut i = 0; + let mut rs = String::new(); + while i < 100000 { + rs.push_str("华华华华华"); + i += 1; + } + rs } - rs + let letters = a_million_letter_x(); + assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); } - let letters = a_million_letter_x(); - assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); -} -#[test] -fn test_slice_2() { - let ss = "中华Việt Nam"; + #[test] + fn test_slice_2() { + let ss = "中华Việt Nam"; + + assert_eq!("华", &ss[3..6]); + assert_eq!("Việt Nam", &ss[6..16]); + + assert_eq!("ab", &"abc"[0..2]); + assert_eq!("bc", &"abc"[1..3]); + assert_eq!("", &"abc"[1..1]); + + assert_eq!("中", &ss[0..3]); + assert_eq!("华V", &ss[3..7]); + assert_eq!("", &ss[3..3]); + /*0: 中 + 3: 华 + 6: V + 7: i + 8: ệ + 11: t + 12: + 13: N + 14: a + 15: m */ + } - assert_eq!("华", &ss[3..6]); - assert_eq!("Việt Nam", &ss[6..16]); + #[test] + #[should_panic] + fn test_slice_fail() { + &"中华Việt Nam"[0..2]; + } - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); + #[test] + #[should_panic] + fn test_str_slice_rangetoinclusive_max_panics() { + &"hello"[..=usize::max_value()]; + } - assert_eq!("中", &ss[0..3]); - assert_eq!("华V", &ss[3..7]); - assert_eq!("", &ss[3..3]); - /*0: 中 - 3: 华 - 6: V - 7: i - 8: ệ - 11: t - 12: - 13: N - 14: a - 15: m */ -} + #[test] + #[should_panic] + fn test_str_slice_rangeinclusive_max_panics() { + &"hello"[1..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_slice_fail() { - &"中华Việt Nam"[0..2]; -} + #[test] + #[should_panic] + fn test_str_slicemut_rangetoinclusive_max_panics() { + let mut s = "hello".to_owned(); + let s: &mut str = &mut s; + &mut s[..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_str_slice_rangetoinclusive_max_panics() { - &"hello"[..=usize::max_value()]; -} + #[test] + #[should_panic] + fn test_str_slicemut_rangeinclusive_max_panics() { + let mut s = "hello".to_owned(); + let s: &mut str = &mut s; + &mut s[1..=usize::max_value()]; + } -#[test] -#[should_panic] -fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[1..=usize::max_value()]; -} + #[test] + fn test_str_get_maxinclusive() { + let mut s = "hello".to_owned(); + { + let s: &str = &s; + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); + } + { + let s: &mut str = &mut s; + assert_eq!(s.get(..=usize::max_value()), None); + assert_eq!(s.get(1..=usize::max_value()), None); + } + } -#[test] -#[should_panic] -fn test_str_slicemut_rangetoinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[..=usize::max_value()]; -} + const LOREM_PARAGRAPH: &'static str = "\ + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ + ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ + eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ + sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ + tempus vel, gravida nec quam."; + + // check the panic includes the prefix of the sliced string + #[test] + #[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] + fn test_slice_fail_truncated_1() { + &LOREM_PARAGRAPH[..1024]; + } + // check the truncation in the panic message + #[test] + #[should_panic(expected="luctus, im`[...]")] + fn test_slice_fail_truncated_2() { + &LOREM_PARAGRAPH[..1024]; + } -#[test] -#[should_panic] -fn test_str_slicemut_rangeinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[1..=usize::max_value()]; -} + #[test] + #[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] + fn test_slice_fail_boundary_1() { + &"abcαβγ"[4..]; + } -#[test] -fn test_str_get_maxinclusive() { - let mut s = "hello".to_owned(); - { - let s: &str = &s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + #[test] + #[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] + fn test_slice_fail_boundary_2() { + &"abcαβγ"[2..6]; } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + + #[test] + fn test_slice_from() { + assert_eq!(&"abcd"[0..], "abcd"); + assert_eq!(&"abcd"[2..], "cd"); + assert_eq!(&"abcd"[4..], ""); + } + #[test] + fn test_slice_to() { + assert_eq!(&"abcd"[..0], ""); + assert_eq!(&"abcd"[..2], "ab"); + assert_eq!(&"abcd"[..4], "abcd"); } } @@ -446,50 +493,6 @@ fn test_is_char_boundary() { } } } -const LOREM_PARAGRAPH: &'static str = "\ -Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ -ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ -eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ -sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ -tempus vel, gravida nec quam."; - -// check the panic includes the prefix of the sliced string -#[test] -#[should_panic(expected="byte index 1024 is out of bounds of `Lorem ipsum dolor sit amet")] -fn test_slice_fail_truncated_1() { - &LOREM_PARAGRAPH[..1024]; -} -// check the truncation in the panic message -#[test] -#[should_panic(expected="luctus, im`[...]")] -fn test_slice_fail_truncated_2() { - &LOREM_PARAGRAPH[..1024]; -} - -#[test] -#[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] -fn test_slice_fail_boundary_1() { - &"abcαβγ"[4..]; -} - -#[test] -#[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] -fn test_slice_fail_boundary_2() { - &"abcαβγ"[2..6]; -} - -#[test] -fn test_slice_from() { - assert_eq!(&"abcd"[0..], "abcd"); - assert_eq!(&"abcd"[2..], "cd"); - assert_eq!(&"abcd"[4..], ""); -} -#[test] -fn test_slice_to() { - assert_eq!(&"abcd"[..0], ""); - assert_eq!(&"abcd"[..2], "ab"); - assert_eq!(&"abcd"[..4], "abcd"); -} #[test] fn test_trim_left_matches() { -- cgit 1.4.1-3-g733a5 From ce66f5d9185aa2b81159fa61597bbb6e4cf2847f Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:08 -0400 Subject: flesh out tests for SliceIndex m*n lines of implementation deserves m*n lines of tests --- src/liballoc/tests/str.rs | 477 +++++++++++++++++++++++++++++++++++---------- src/libcore/tests/slice.rs | 282 +++++++++++++++++++++++---- 2 files changed, 622 insertions(+), 137 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index c9536cbe168..bfba9a6b393 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -292,19 +292,194 @@ fn test_replace_pattern() { } mod slice_index { + // Test a slicing operation **that should succeed,** + // testing it on all of the indexing methods. + // + // DO NOT use this in `should_panic` tests, unless you are testing the macro itself. + macro_rules! assert_range_eq { + ($s:expr, $range:expr, $expected:expr) + => { + let mut s: String = $s.to_owned(); + let mut expected: String = $expected.to_owned(); + { + let s: &str = &s; + let expected: &str = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut str = &mut s; + let expected: &mut str = &mut expected; + + assert_eq!( + &mut s[$range], expected, + "(in assertion for: index_mut)", + ); + assert_eq!( + s.get_mut($range), Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + } + } + + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of bounds")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!("abc", 0..5, "abc"); + } + + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!("abc", 0..2, "abc"); + } + + // Generates test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + mod $case_name:ident { + let DATA = $data:expr; + + // optional: + // + // a similar input for which DATA[input] succeeds, and the corresponding + // output str. This helps validate "critical points" where an input range + // straddles the boundary between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + let GOOD_INPUT = $good:expr; + let GOOD_OUTPUT = $output:expr; + )* + + let BAD_INPUT = $bad:expr; + const EXPECT_MSG = $expect_msg:expr; // must be a literal + + !!generate_tests!! + } + )*) => {$( + mod $case_name { + #[test] + fn pass() { + let mut v: String = $data.into(); + + $( assert_range_eq!(v, $good, $output); )* + + { + let v: &str = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut str = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v: String = $data.into(); + let v: &str = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v: String = $data.into(); + let v: &mut str = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple_ascii() { + assert_range_eq!("abc", .., "abc"); + + assert_range_eq!("abc", 0..2, "ab"); + assert_range_eq!("abc", 0..=1, "ab"); + assert_range_eq!("abc", ..2, "ab"); + assert_range_eq!("abc", ..=1, "ab"); + + assert_range_eq!("abc", 1..3, "bc"); + assert_range_eq!("abc", 1..=2, "bc"); + assert_range_eq!("abc", 1..1, ""); + assert_range_eq!("abc", 1..=0, ""); + } + #[test] - fn test_slice() { - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - assert_eq!("\u{65e5}", &"\u{65e5}\u{672c}"[0..3]); + fn simple_unicode() { + // 日本 + assert_range_eq!("\u{65e5}\u{672c}", .., "\u{65e5}\u{672c}"); + + assert_range_eq!("\u{65e5}\u{672c}", 0..3, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", 0..=2, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", ..3, "\u{65e5}"); + assert_range_eq!("\u{65e5}\u{672c}", ..=2, "\u{65e5}"); + + assert_range_eq!("\u{65e5}\u{672c}", 3..6, "\u{672c}"); + assert_range_eq!("\u{65e5}\u{672c}", 3..=5, "\u{672c}"); + assert_range_eq!("\u{65e5}\u{672c}", 3.., "\u{672c}"); let data = "ประเทศไทย中华"; - assert_eq!("ป", &data[0..3]); - assert_eq!("ร", &data[3..6]); - assert_eq!("", &data[3..3]); - assert_eq!("华", &data[30..33]); + assert_range_eq!(data, 0..3, "ป"); + assert_range_eq!(data, 3..6, "ร"); + assert_range_eq!(data, 3..3, ""); + assert_range_eq!(data, 30..33, "华"); + /*0: 中 + 3: 华 + 6: V + 7: i + 8: ệ + 11: t + 12: + 13: N + 14: a + 15: m */ + let ss = "中华Việt Nam"; + assert_range_eq!(ss, 3..6, "华"); + assert_range_eq!(ss, 6..16, "Việt Nam"); + assert_range_eq!(ss, 6..=15, "Việt Nam"); + assert_range_eq!(ss, 6.., "Việt Nam"); + + assert_range_eq!(ss, 0..3, "中"); + assert_range_eq!(ss, 3..7, "华V"); + assert_range_eq!(ss, 3..=6, "华V"); + assert_range_eq!(ss, 3..3, ""); + assert_range_eq!(ss, 3..=2, ""); + } + + #[test] + fn simple_big() { fn a_million_letter_x() -> String { let mut i = 0; let mut rs = String::new(); @@ -324,33 +499,7 @@ mod slice_index { rs } let letters = a_million_letter_x(); - assert_eq!(half_a_million_letter_x(), &letters[0..3 * 500000]); - } - - #[test] - fn test_slice_2() { - let ss = "中华Việt Nam"; - - assert_eq!("华", &ss[3..6]); - assert_eq!("Việt Nam", &ss[6..16]); - - assert_eq!("ab", &"abc"[0..2]); - assert_eq!("bc", &"abc"[1..3]); - assert_eq!("", &"abc"[1..1]); - - assert_eq!("中", &ss[0..3]); - assert_eq!("华V", &ss[3..7]); - assert_eq!("", &ss[3..3]); - /*0: 中 - 3: 华 - 6: V - 7: i - 8: ệ - 11: t - 12: - 13: N - 14: a - 15: m */ + assert_range_eq!(letters, 0..3 * 500000, half_a_million_letter_x()); } #[test] @@ -359,55 +508,210 @@ mod slice_index { &"中华Việt Nam"[0..2]; } - #[test] - #[should_panic] - fn test_str_slice_rangetoinclusive_max_panics() { - &"hello"[..=usize::max_value()]; - } + panic_cases! { + mod rangefrom_len { + let DATA = "abcdef"; - #[test] - #[should_panic] - fn test_str_slice_rangeinclusive_max_panics() { - &"hello"[1..=usize::max_value()]; - } + let GOOD_INPUT = 6..; + let GOOD_OUTPUT = ""; - #[test] - #[should_panic] - fn test_str_slicemut_rangetoinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[..=usize::max_value()]; + let BAD_INPUT = 7..; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangeto_len { + let DATA = "abcdef"; + + let GOOD_INPUT = ..6; + let GOOD_OUTPUT = "abcdef"; + + let BAD_INPUT = ..7; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangetoinclusive_len { + let DATA = "abcdef"; + + let GOOD_INPUT = ..=5; + let GOOD_OUTPUT = "abcdef"; + + let BAD_INPUT = ..=6; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod range_len_len { + let DATA = "abcdef"; + + let GOOD_INPUT = 6..6; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 7..7; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } + + mod rangeinclusive_len_len { + let DATA = "abcdef"; + + let GOOD_INPUT = 6..=5; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 7..=6; + const EXPECT_MSG = "out of bounds"; + + !!generate_tests!! + } } - #[test] - #[should_panic] - fn test_str_slicemut_rangeinclusive_max_panics() { - let mut s = "hello".to_owned(); - let s: &mut str = &mut s; - &mut s[1..=usize::max_value()]; + panic_cases! { + mod range_neg_width { + let DATA = "abcdef"; + + let GOOD_INPUT = 4..4; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 4..3; + const EXPECT_MSG = "begin <= end (4 <= 3)"; + + !!generate_tests!! + } + + mod rangeinclusive_neg_width { + let DATA = "abcdef"; + + let GOOD_INPUT = 4..=3; + let GOOD_OUTPUT = ""; + + let BAD_INPUT = 4..=2; + const EXPECT_MSG = "begin <= end (4 <= 3)"; + + !!generate_tests!! + } } - #[test] - fn test_str_get_maxinclusive() { - let mut s = "hello".to_owned(); - { - let s: &str = &s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + mod overflow { + panic_cases! { + + mod rangeinclusive { + let DATA = "hello"; + + let BAD_INPUT = 1..=usize::max_value(); + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + + mod rangetoinclusive { + let DATA = "hello"; + + let BAD_INPUT = ..=usize::max_value(); + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } } - { - let s: &mut str = &mut s; - assert_eq!(s.get(..=usize::max_value()), None); - assert_eq!(s.get(1..=usize::max_value()), None); + } + + mod boundary { + const DATA: &'static str = "abcαβγ"; + + const BAD_START: usize = 4; + const GOOD_START: usize = 3; + const BAD_END: usize = 6; + const GOOD_END: usize = 7; + const BAD_END_INCL: usize = BAD_END - 1; + const GOOD_END_INCL: usize = GOOD_END - 1; + + // it is especially important to test all of the different range types here + // because some of the logic may be duplicated as part of micro-optimizations + // to dodge unicode boundary checks on half-ranges. + panic_cases! { + mod range_1 { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..super::GOOD_END; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod range_2 { + let DATA = super::DATA; + + let BAD_INPUT = super::GOOD_START..super::BAD_END; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangefrom { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod rangeto { + let DATA = super::DATA; + + let BAD_INPUT = ..super::BAD_END; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangeinclusive_1 { + let DATA = super::DATA; + + let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL; + const EXPECT_MSG = + "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; + + !!generate_tests!! + } + + mod rangeinclusive_2 { + let DATA = super::DATA; + + let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } + + mod rangetoinclusive { + let DATA = super::DATA; + + let BAD_INPUT = ..=super::BAD_END_INCL; + const EXPECT_MSG = + "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; + + !!generate_tests!! + } } } const LOREM_PARAGRAPH: &'static str = "\ - Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem sit amet dolor \ - ultricies condimentum. Praesent iaculis purus elit, ac malesuada quam malesuada in. Duis sed orci \ - eros. Suspendisse sit amet magna mollis, mollis nunc luctus, imperdiet mi. Integer fringilla non \ - sem ut lacinia. Fusce varius tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec \ - tempus vel, gravida nec quam."; + Lorem ipsum dolor sit amet, consectetur adipiscing elit. Suspendisse quis lorem \ + sit amet dolor ultricies condimentum. Praesent iaculis purus elit, ac malesuada \ + quam malesuada in. Duis sed orci eros. Suspendisse sit amet magna mollis, mollis \ + nunc luctus, imperdiet mi. Integer fringilla non sem ut lacinia. Fusce varius \ + tortor a risus porttitor hendrerit. Morbi mauris dui, ultricies nec tempus vel, \ + gravida nec quam."; // check the panic includes the prefix of the sliced string #[test] @@ -421,31 +725,6 @@ mod slice_index { fn test_slice_fail_truncated_2() { &LOREM_PARAGRAPH[..1024]; } - - #[test] - #[should_panic(expected="byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of")] - fn test_slice_fail_boundary_1() { - &"abcαβγ"[4..]; - } - - #[test] - #[should_panic(expected="byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of")] - fn test_slice_fail_boundary_2() { - &"abcαβγ"[2..6]; - } - - #[test] - fn test_slice_from() { - assert_eq!(&"abcd"[0..], "abcd"); - assert_eq!(&"abcd"[2..], "cd"); - assert_eq!(&"abcd"[4..], ""); - } - #[test] - fn test_slice_to() { - assert_eq!(&"abcd"[..0], ""); - assert_eq!(&"abcd"[..2], "ab"); - assert_eq!(&"abcd"[..4], "abcd"); - } } #[test] diff --git a/src/libcore/tests/slice.rs b/src/libcore/tests/slice.rs index 53fdfa06827..5272c7427d9 100644 --- a/src/libcore/tests/slice.rs +++ b/src/libcore/tests/slice.rs @@ -376,48 +376,254 @@ fn test_windows_zip() { assert_eq!(res, [14, 18, 22, 26]); } -#[test] -fn get_range() { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - assert_eq!(v.get(..), Some(&[0, 1, 2, 3, 4, 5][..])); - assert_eq!(v.get(..2), Some(&[0, 1][..])); - assert_eq!(v.get(2..), Some(&[2, 3, 4, 5][..])); - assert_eq!(v.get(1..4), Some(&[1, 2, 3][..])); - assert_eq!(v.get(7..), None); - assert_eq!(v.get(7..10), None); -} +mod slice_index { + // Test a slicing operation that should succeed, + // testing it on all of the indexing methods. + macro_rules! assert_range_eq { + ($arr:expr, $range:expr, $expected:expr) + => { + let mut arr = $arr; + let mut expected = $expected; + { + let s: &[_] = &arr; + let expected: &[_] = &expected; + + assert_eq!(&s[$range], expected, "(in assertion for: index)"); + assert_eq!(s.get($range), Some(expected), "(in assertion for: get)"); + unsafe { + assert_eq!( + s.get_unchecked($range), expected, + "(in assertion for: get_unchecked)", + ); + } + } + { + let s: &mut [_] = &mut arr; + let expected: &mut [_] = &mut expected; + + assert_eq!( + &mut s[$range], expected, + "(in assertion for: index_mut)", + ); + assert_eq!( + s.get_mut($range), Some(&mut expected[..]), + "(in assertion for: get_mut)", + ); + unsafe { + assert_eq!( + s.get_unchecked_mut($range), expected, + "(in assertion for: get_unchecked_mut)", + ); + } + } + } + } -#[test] -fn get_mut_range() { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_mut(..), Some(&mut [0, 1, 2, 3, 4, 5][..])); - assert_eq!(v.get_mut(..2), Some(&mut [0, 1][..])); - assert_eq!(v.get_mut(2..), Some(&mut [2, 3, 4, 5][..])); - assert_eq!(v.get_mut(1..4), Some(&mut [1, 2, 3][..])); - assert_eq!(v.get_mut(7..), None); - assert_eq!(v.get_mut(7..10), None); -} - -#[test] -fn get_unchecked_range() { - unsafe { - let v: &[i32] = &[0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_unchecked(..), &[0, 1, 2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked(..2), &[0, 1][..]); - assert_eq!(v.get_unchecked(2..), &[2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked(1..4), &[1, 2, 3][..]); + // Make sure the macro can actually detect bugs, + // because if it can't, then what are we even doing here? + // + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "out of range")] + fn assert_range_eq_can_fail_by_panic() { + assert_range_eq!([0, 1, 2], 0..5, [0, 1, 2]); } -} -#[test] -fn get_unchecked_mut_range() { - unsafe { - let v: &mut [i32] = &mut [0, 1, 2, 3, 4, 5]; - assert_eq!(v.get_unchecked_mut(..), &mut [0, 1, 2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked_mut(..2), &mut [0, 1][..]); - assert_eq!(v.get_unchecked_mut(2..), &mut[2, 3, 4, 5][..]); - assert_eq!(v.get_unchecked_mut(1..4), &mut [1, 2, 3][..]); + // (Be aware this only demonstrates the ability to detect bugs + // in the FIRST method it calls, as the macro is not designed + // to be used in `should_panic`) + #[test] + #[should_panic(expected = "==")] + fn assert_range_eq_can_fail_by_inequality() { + assert_range_eq!([0, 1, 2], 0..2, [0, 1, 2]); + } + + // Test cases for bad index operations. + // + // This generates `should_panic` test cases for Index/IndexMut + // and `None` test cases for get/get_mut. + macro_rules! panic_cases { + ($( + mod $case_name:ident { + let DATA: $Data: ty = $data:expr; + + // optional: + // + // a similar input for which DATA[input] succeeds, and the corresponding + // output as an array. This helps validate "critical points" where an + // input range straddles the boundary between valid and invalid. + // (such as the input `len..len`, which is just barely valid) + $( + let GOOD_INPUT = $good:expr; + let GOOD_OUTPUT = $output:expr; + )* + + let BAD_INPUT = $bad:expr; + const EXPECT_MSG = $expect_msg:expr; + + !!generate_tests!! + } + )*) => {$( + mod $case_name { + #[test] + fn pass() { + let mut v: $Data = $data; + + $( assert_range_eq!($data, $good, $output); )* + + { + let v: &[_] = &v; + assert_eq!(v.get($bad), None, "(in None assertion for get)"); + } + + { + let v: &mut [_] = &mut v; + assert_eq!(v.get_mut($bad), None, "(in None assertion for get_mut)"); + } + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_fail() { + let v: $Data = $data; + let v: &[_] = &v; + let _v = &v[$bad]; + } + + #[test] + #[should_panic(expected = $expect_msg)] + fn index_mut_fail() { + let mut v: $Data = $data; + let v: &mut [_] = &mut v; + let _v = &mut v[$bad]; + } + } + )*}; + } + + #[test] + fn simple() { + let v = [0, 1, 2, 3, 4, 5]; + + assert_range_eq!(v, .., [0, 1, 2, 3, 4, 5]); + assert_range_eq!(v, ..2, [0, 1]); + assert_range_eq!(v, ..=1, [0, 1]); + assert_range_eq!(v, 2.., [2, 3, 4, 5]); + assert_range_eq!(v, 1..4, [1, 2, 3]); + assert_range_eq!(v, 1..=3, [1, 2, 3]); + } + + panic_cases! { + mod rangefrom_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..; + const EXPECT_MSG = "but ends at"; // perhaps not ideal + + !!generate_tests!! + } + + mod rangeto_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = ..6; + let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + + let BAD_INPUT = ..7; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod rangetoinclusive_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = ..=5; + let GOOD_OUTPUT = [0, 1, 2, 3, 4, 5]; + + let BAD_INPUT = ..=6; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod range_len_len { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..6; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..7; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } + + mod rangeinclusive_len_len{ + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 6..=5; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 7..=6; + const EXPECT_MSG = "out of range"; + + !!generate_tests!! + } } + + panic_cases! { + mod range_neg_width { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 4..4; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 4..3; + const EXPECT_MSG = "but ends at"; + + !!generate_tests!! + } + + mod rangeinclusive_neg_width { + let DATA: [i32; 6] = [0, 1, 2, 3, 4, 5]; + + let GOOD_INPUT = 4..=3; + let GOOD_OUTPUT = []; + + let BAD_INPUT = 4..=2; + const EXPECT_MSG = "but ends at"; + + !!generate_tests!! + } + } + + panic_cases! { + mod rangeinclusive_overflow { + let DATA: [i32; 2] = [0, 1]; + + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + let BAD_INPUT = 0 ..= ::std::usize::MAX; + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + + mod rangetoinclusive_overflow { + let DATA: [i32; 2] = [0, 1]; + + let BAD_INPUT = ..= ::std::usize::MAX; + const EXPECT_MSG = "maximum usize"; + + !!generate_tests!! + } + } // panic_cases! } #[test] -- cgit 1.4.1-3-g733a5 From 02b3da1200df47ea7343dd2cd960b8afe983ac9c Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:19 -0400 Subject: decrease false negatives for str overflow test --- src/liballoc/tests/str.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index bfba9a6b393..696ce79f369 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -602,7 +602,9 @@ mod slice_index { mod rangeinclusive { let DATA = "hello"; - let BAD_INPUT = 1..=usize::max_value(); + // note: using 0 specifically ensures that the result of overflowing is 0..0, + // so that `get` doesn't simply return None for the wrong reason. + let BAD_INPUT = 0..=usize::max_value(); const EXPECT_MSG = "maximum usize"; !!generate_tests!! -- cgit 1.4.1-3-g733a5 From fba903a435ea6e0e3736541cb487586262835e48 Mon Sep 17 00:00:00 2001 From: kennytm Date: Fri, 6 Apr 2018 02:03:22 +0800 Subject: Make the fields of RangeInclusive private. Added new()/start()/end() methods to RangeInclusive. Changed the lowering of `..=` to use RangeInclusive::new(). --- src/liballoc/lib.rs | 2 +- src/liballoc/tests/lib.rs | 2 +- src/libcore/ops/range.rs | 60 +++++++++++++++++++++++++++++++++++++++++++- src/libcore/tests/lib.rs | 2 +- src/librustc/hir/lowering.rs | 16 +++++++++++- src/librustc_trans/lib.rs | 2 +- 6 files changed, 78 insertions(+), 6 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 021395d0c82..c94fe2a2f83 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -122,7 +122,7 @@ #![feature(on_unimplemented)] #![feature(exact_chunks)] #![feature(pointer_methods)] -#![feature(inclusive_range_fields)] +#![feature(inclusive_range_methods)] #![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(not(test), feature(fn_traits, i128))] diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 32272169307..1c8ff316e55 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -25,7 +25,7 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(exact_chunks)] -#![feature(inclusive_range_fields)] +#![feature(inclusive_range_methods)] extern crate alloc_system; extern crate core; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index d70f7ae66f9..c1bd1ef2d1d 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -320,7 +320,7 @@ impl> RangeTo { /// ``` /// #![feature(inclusive_range_fields)] /// -/// assert_eq!((3..=5), std::ops::RangeInclusive { start: 3, end: 5 }); +/// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// /// let arr = [0, 1, 2, 3]; @@ -331,14 +331,72 @@ impl> RangeTo { #[derive(Clone, PartialEq, Eq, Hash)] // not Copy -- see #27186 #[stable(feature = "inclusive_range", since = "1.26.0")] pub struct RangeInclusive { + // FIXME: The current representation follows RFC 1980, + // but it is known that LLVM is not able to optimize loops following that RFC. + // Consider adding an extra `bool` field to indicate emptiness of the range. + // See #45222 for performance test cases. + #[cfg(not(stage0))] + pub(crate) start: Idx, + #[cfg(not(stage0))] + pub(crate) end: Idx, /// The lower bound of the range (inclusive). + #[cfg(stage0)] #[unstable(feature = "inclusive_range_fields", issue = "49022")] pub start: Idx, /// The upper bound of the range (inclusive). + #[cfg(stage0)] #[unstable(feature = "inclusive_range_fields", issue = "49022")] pub end: Idx, } +impl RangeInclusive { + /// Creates a new inclusive range. Equivalent to writing `start..=end`. + /// + /// # Examples + /// + /// ``` + /// #![feature(inclusive_range_methods)] + /// use std::ops::RangeInclusive; + /// + /// assert_eq!(3..=5, RangeInclusive::new(3, 5)); + /// ``` + #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[inline] + pub fn new(start: Idx, end: Idx) -> Self { + Self { start, end } + } + + /// Returns the lower bound of the range (inclusive). + /// + /// # Examples + /// + /// ``` + /// #![feature(inclusive_range_methods)] + /// + /// assert_eq!((3..=5).start(), &3); + /// ``` + #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[inline] + pub fn start(&self) -> &Idx { + &self.start + } + + /// Returns the upper bound of the range (inclusive). + /// + /// # Examples + /// + /// ``` + /// #![feature(inclusive_range_methods)] + /// + /// assert_eq!((3..=5).end(), &5); + /// ``` + #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[inline] + pub fn end(&self) -> &Idx { + &self.end + } +} + #[stable(feature = "inclusive_range", since = "1.26.0")] impl fmt::Debug for RangeInclusive { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index e4d27717938..f6750c590b3 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -44,7 +44,7 @@ #![feature(exact_chunks)] #![cfg_attr(stage0, feature(atomic_nand))] #![feature(reverse_bits)] -#![feature(inclusive_range_fields)] +#![feature(inclusive_range_methods)] #![feature(iterator_find_map)] extern crate core; diff --git a/src/librustc/hir/lowering.rs b/src/librustc/hir/lowering.rs index e4b9fc1385d..196f7879980 100644 --- a/src/librustc/hir/lowering.rs +++ b/src/librustc/hir/lowering.rs @@ -3119,6 +3119,20 @@ impl<'a> LoweringContext<'a> { ExprKind::Index(ref el, ref er) => { hir::ExprIndex(P(self.lower_expr(el)), P(self.lower_expr(er))) } + // Desugar `..=` to `std::ops::RangeInclusive::new(, )` + ExprKind::Range(Some(ref e1), Some(ref e2), RangeLimits::Closed) => { + // FIXME: Use head_sp directly after RangeInclusive::new() is stabilized in stage0. + let span = self.allow_internal_unstable(CompilerDesugaringKind::DotFill, e.span); + let id = self.lower_node_id(e.id); + let e1 = self.lower_expr(e1); + let e2 = self.lower_expr(e2); + let ty_path = P(self.std_path(span, &["ops", "RangeInclusive"], false)); + let ty = self.ty_path(id, span, hir::QPath::Resolved(None, ty_path)); + let new_seg = P(hir::PathSegment::from_name(Symbol::intern("new"))); + let new_path = hir::QPath::TypeRelative(ty, new_seg); + let new = P(self.expr(span, hir::ExprPath(new_path), ThinVec::new())); + hir::ExprCall(new, hir_vec![e1, e2]) + } ExprKind::Range(ref e1, ref e2, lims) => { use syntax::ast::RangeLimits::*; @@ -3128,7 +3142,7 @@ impl<'a> LoweringContext<'a> { (&None, &Some(..), HalfOpen) => "RangeTo", (&Some(..), &Some(..), HalfOpen) => "Range", (&None, &Some(..), Closed) => "RangeToInclusive", - (&Some(..), &Some(..), Closed) => "RangeInclusive", + (&Some(..), &Some(..), Closed) => unreachable!(), (_, &None, Closed) => self.diagnostic() .span_fatal(e.span, "inclusive range with no end") .raise(), diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs index 96a10e8b99d..9259fef279f 100644 --- a/src/librustc_trans/lib.rs +++ b/src/librustc_trans/lib.rs @@ -29,7 +29,7 @@ #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] #![feature(optin_builtin_traits)] -#![feature(inclusive_range_fields)] +#![feature(inclusive_range_methods)] use rustc::dep_graph::WorkProduct; use syntax_pos::symbol::Symbol; -- cgit 1.4.1-3-g733a5 From f1d7b453fed6acefc68f90752922b37c6e3ac7a4 Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Mon, 30 Apr 2018 07:37:36 -0400 Subject: revise test gen macro for str --- src/liballoc/tests/str.rs | 225 +++++++++++++++++----------------------------- 1 file changed, 81 insertions(+), 144 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 696ce79f369..2edd41a70b9 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -291,11 +291,14 @@ fn test_replace_pattern() { assert_eq!(data.replace(|c| c == 'γ', "😺😺😺"), "abcdαβ😺😺😺δabcdαβ😺😺😺δ"); } +// The current implementation of SliceIndex fails to handle methods +// orthogonally from range types; therefore, it is worth testing +// all of the indexing operations on each input. mod slice_index { // Test a slicing operation **that should succeed,** // testing it on all of the indexing methods. // - // DO NOT use this in `should_panic` tests, unless you are testing the macro itself. + // This is not suitable for testing failure on invalid inputs. macro_rules! assert_range_eq { ($s:expr, $range:expr, $expected:expr) => { @@ -340,7 +343,7 @@ mod slice_index { // because if it can't, then what are we even doing here? // // (Be aware this only demonstrates the ability to detect bugs - // in the FIRST method it calls, as the macro is not designed + // in the FIRST method that panics, as the macro is not designed // to be used in `should_panic`) #[test] #[should_panic(expected = "out of bounds")] @@ -363,8 +366,8 @@ mod slice_index { // and `None` test cases for get/get_mut. macro_rules! panic_cases { ($( - mod $case_name:ident { - let DATA = $data:expr; + in mod $case_name:ident { + data: $data:expr; // optional: // @@ -373,14 +376,11 @@ mod slice_index { // straddles the boundary between valid and invalid. // (such as the input `len..len`, which is just barely valid) $( - let GOOD_INPUT = $good:expr; - let GOOD_OUTPUT = $output:expr; + good: data[$good:expr] == $output:expr; )* - let BAD_INPUT = $bad:expr; - const EXPECT_MSG = $expect_msg:expr; // must be a literal - - !!generate_tests!! + bad: data[$bad:expr]; + message: $expect_msg:expr; // must be a literal } )*) => {$( mod $case_name { @@ -509,114 +509,72 @@ mod slice_index { } panic_cases! { - mod rangefrom_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangefrom_len { + data: "abcdef"; + good: data[6..] == ""; + bad: data[7..]; + message: "out of bounds"; } - mod rangeto_len { - let DATA = "abcdef"; - - let GOOD_INPUT = ..6; - let GOOD_OUTPUT = "abcdef"; - - let BAD_INPUT = ..7; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangeto_len { + data: "abcdef"; + good: data[..6] == "abcdef"; + bad: data[..7]; + message: "out of bounds"; } - mod rangetoinclusive_len { - let DATA = "abcdef"; - - let GOOD_INPUT = ..=5; - let GOOD_OUTPUT = "abcdef"; - - let BAD_INPUT = ..=6; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangetoinclusive_len { + data: "abcdef"; + good: data[..=5] == "abcdef"; + bad: data[..=6]; + message: "out of bounds"; } - mod range_len_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..6; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..7; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod range_len_len { + data: "abcdef"; + good: data[6..6] == ""; + bad: data[7..7]; + message: "out of bounds"; } - mod rangeinclusive_len_len { - let DATA = "abcdef"; - - let GOOD_INPUT = 6..=5; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 7..=6; - const EXPECT_MSG = "out of bounds"; - - !!generate_tests!! + in mod rangeinclusive_len_len { + data: "abcdef"; + good: data[6..=5] == ""; + bad: data[7..=6]; + message: "out of bounds"; } } panic_cases! { - mod range_neg_width { - let DATA = "abcdef"; - - let GOOD_INPUT = 4..4; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 4..3; - const EXPECT_MSG = "begin <= end (4 <= 3)"; - - !!generate_tests!! + in mod range_neg_width { + data: "abcdef"; + good: data[4..4] == ""; + bad: data[4..3]; + message: "begin <= end (4 <= 3)"; } - mod rangeinclusive_neg_width { - let DATA = "abcdef"; - - let GOOD_INPUT = 4..=3; - let GOOD_OUTPUT = ""; - - let BAD_INPUT = 4..=2; - const EXPECT_MSG = "begin <= end (4 <= 3)"; - - !!generate_tests!! + in mod rangeinclusive_neg_width { + data: "abcdef"; + good: data[4..=3] == ""; + bad: data[4..=2]; + message: "begin <= end (4 <= 3)"; } } mod overflow { panic_cases! { - - mod rangeinclusive { - let DATA = "hello"; - + in mod rangeinclusive { + data: "hello"; // note: using 0 specifically ensures that the result of overflowing is 0..0, // so that `get` doesn't simply return None for the wrong reason. - let BAD_INPUT = 0..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + bad: data[0..=usize::max_value()]; + message: "maximum usize"; } - mod rangetoinclusive { - let DATA = "hello"; - - let BAD_INPUT = ..=usize::max_value(); - const EXPECT_MSG = "maximum usize"; - - !!generate_tests!! + in mod rangetoinclusive { + data: "hello"; + bad: data[..=usize::max_value()]; + message: "maximum usize"; } } } @@ -635,74 +593,53 @@ mod slice_index { // because some of the logic may be duplicated as part of micro-optimizations // to dodge unicode boundary checks on half-ranges. panic_cases! { - mod range_1 { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..super::GOOD_END; - const EXPECT_MSG = + in mod range_1 { + data: super::DATA; + bad: data[super::BAD_START..super::GOOD_END]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod range_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..super::BAD_END; - const EXPECT_MSG = + in mod range_2 { + data: super::DATA; + bad: data[super::GOOD_START..super::BAD_END]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangefrom { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..; - const EXPECT_MSG = + in mod rangefrom { + data: super::DATA; + bad: data[super::BAD_START..]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod rangeto { - let DATA = super::DATA; - - let BAD_INPUT = ..super::BAD_END; - const EXPECT_MSG = + in mod rangeto { + data: super::DATA; + bad: data[..super::BAD_END]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangeinclusive_1 { - let DATA = super::DATA; - - let BAD_INPUT = super::BAD_START..=super::GOOD_END_INCL; - const EXPECT_MSG = + in mod rangeinclusive_1 { + data: super::DATA; + bad: data[super::BAD_START..=super::GOOD_END_INCL]; + message: "byte index 4 is not a char boundary; it is inside 'α' (bytes 3..5) of"; - - !!generate_tests!! } - mod rangeinclusive_2 { - let DATA = super::DATA; - - let BAD_INPUT = super::GOOD_START..=super::BAD_END_INCL; - const EXPECT_MSG = + in mod rangeinclusive_2 { + data: super::DATA; + bad: data[super::GOOD_START..=super::BAD_END_INCL]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } - mod rangetoinclusive { - let DATA = super::DATA; - - let BAD_INPUT = ..=super::BAD_END_INCL; - const EXPECT_MSG = + in mod rangetoinclusive { + data: super::DATA; + bad: data[..=super::BAD_END_INCL]; + message: "byte index 6 is not a char boundary; it is inside 'β' (bytes 5..7) of"; - - !!generate_tests!! } } } -- cgit 1.4.1-3-g733a5 From 160063aad2206a35e6312bfaa159f0f4475af0ae Mon Sep 17 00:00:00 2001 From: F001 Date: Sat, 5 May 2018 16:34:43 +0800 Subject: make `String::new()` const --- src/liballoc/lib.rs | 1 + src/liballoc/string.rs | 3 ++- src/test/run-pass/collections-const-new.rs | 22 ++++++++++++++++++++++ src/test/run-pass/vec-const-new.rs | 17 ----------------- 4 files changed, 25 insertions(+), 18 deletions(-) create mode 100644 src/test/run-pass/collections-const-new.rs delete mode 100644 src/test/run-pass/vec-const-new.rs (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index da4b76a4d52..bb78c14b905 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -125,6 +125,7 @@ #![feature(inclusive_range_methods)] #![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(rustc_const_unstable)] +#![feature(const_vec_new)] #![cfg_attr(not(test), feature(fn_traits, i128))] #![cfg_attr(test, feature(test))] diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 2f84d5f7f86..da9afdd2ca3 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -380,7 +380,8 @@ impl String { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> String { + #[rustc_const_unstable(feature = "const_string_new")] + pub const fn new() -> String { String { vec: Vec::new() } } diff --git a/src/test/run-pass/collections-const-new.rs b/src/test/run-pass/collections-const-new.rs new file mode 100644 index 00000000000..4003c2ec4f7 --- /dev/null +++ b/src/test/run-pass/collections-const-new.rs @@ -0,0 +1,22 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test several functions can be used for constants +// 1. Vec::new() +// 2. String::new() + +#![feature(const_vec_new)] +#![feature(const_string_new)] + +const MY_VEC: Vec = Vec::new(); + +const MY_STRING: String = String::new(); + +pub fn main() {} diff --git a/src/test/run-pass/vec-const-new.rs b/src/test/run-pass/vec-const-new.rs deleted file mode 100644 index 62e2a36d7cc..00000000000 --- a/src/test/run-pass/vec-const-new.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that Vec::new() can be used for constants - -#![feature(const_vec_new)] - -const MY_VEC: Vec = Vec::new(); - -pub fn main() {} -- cgit 1.4.1-3-g733a5 From 9f8f366eeabee6273abf3628bb75ecd3b4b57f22 Mon Sep 17 00:00:00 2001 From: Nikita Popov Date: Sun, 6 May 2018 16:55:04 +0200 Subject: Use ManuallyDrop instead of Option in Hole implementation The Option is always Some until drop, where it becomes None. Make this more explicit and avoid unwraps by using ManuallyDrop. This change should be performance-neutral as LLVM already optimizes the unwraps away in the inlined code. --- src/liballoc/binary_heap.rs | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs index 668b61c51d8..fcadcb544c4 100644 --- a/src/liballoc/binary_heap.rs +++ b/src/liballoc/binary_heap.rs @@ -157,7 +157,7 @@ use core::ops::{Deref, DerefMut}; use core::iter::{FromIterator, FusedIterator}; -use core::mem::{swap, size_of}; +use core::mem::{swap, size_of, ManuallyDrop}; use core::ptr; use core::fmt; @@ -864,8 +864,7 @@ impl BinaryHeap { /// position with the value that was originally removed. struct Hole<'a, T: 'a> { data: &'a mut [T], - /// `elt` is always `Some` from new until drop. - elt: Option, + elt: ManuallyDrop, pos: usize, } @@ -879,7 +878,7 @@ impl<'a, T> Hole<'a, T> { let elt = ptr::read(&data[pos]); Hole { data, - elt: Some(elt), + elt: ManuallyDrop::new(elt), pos, } } @@ -892,7 +891,7 @@ impl<'a, T> Hole<'a, T> { /// Returns a reference to the element removed. #[inline] fn element(&self) -> &T { - self.elt.as_ref().unwrap() + &self.elt } /// Returns a reference to the element at `index`. @@ -925,7 +924,7 @@ impl<'a, T> Drop for Hole<'a, T> { // fill the hole again unsafe { let pos = self.pos; - ptr::write(self.data.get_unchecked_mut(pos), self.elt.take().unwrap()); + ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); } } } -- cgit 1.4.1-3-g733a5 From 9f26376281035fde5bf332a4326fadcdb48b7ef7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 7 May 2018 12:44:26 +0200 Subject: Rename Pin to PinMut MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit As discussed at [1] §3 and [2] and [3], a formal look at pinning requires considering a distinguished "shared pinned" mode/typestate. Given that, it seems desirable to at least eventually actually expose that typestate as a reference type. This renames Pin to PinMut, freeing the name Pin in case we want to use it for a shared pinned reference later on. [1] https://www.ralfj.de/blog/2018/04/10/safe-intrusive-collections-with-pinning.html [2] https://github.com/rust-lang/rfcs/pull/2349#issuecomment-379250361 [3] https://github.com/rust-lang/rust/issues/49150#issuecomment-380488275 --- src/liballoc/boxed.rs | 6 +++--- src/libcore/mem.rs | 48 ++++++++++++++++++++++++------------------------ 2 files changed, 27 insertions(+), 27 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 1b4f86dcfac..a1567344235 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -62,7 +62,7 @@ use core::fmt; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::{Unpin, Unsize}; -use core::mem::{self, Pin}; +use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; use core::convert::From; @@ -771,8 +771,8 @@ impl PinBox { #[unstable(feature = "pin", issue = "49150")] impl PinBox { /// Get a pinned reference to the data in this PinBox. - pub fn as_pin<'a>(&'a mut self) -> Pin<'a, T> { - unsafe { Pin::new_unchecked(&mut *self.inner) } + pub fn as_pin_mut<'a>(&'a mut self) -> PinMut<'a, T> { + unsafe { PinMut::new_unchecked(&mut *self.inner) } } /// Get a mutable reference to the data inside this PinBox. diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 10efab82ddf..5d344c48cb8 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1101,53 +1101,53 @@ impl ::hash::Hash for ManuallyDrop { /// value implements the `Unpin` trait. #[unstable(feature = "pin", issue = "49150")] #[fundamental] -pub struct Pin<'a, T: ?Sized + 'a> { +pub struct PinMut<'a, T: ?Sized + 'a> { inner: &'a mut T, } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized + Unpin> Pin<'a, T> { - /// Construct a new `Pin` around a reference to some data of a type that +impl<'a, T: ?Sized + Unpin> PinMut<'a, T> { + /// Construct a new `PinMut` around a reference to some data of a type that /// implements `Unpin`. #[unstable(feature = "pin", issue = "49150")] - pub fn new(reference: &'a mut T) -> Pin<'a, T> { - Pin { inner: reference } + pub fn new(reference: &'a mut T) -> PinMut<'a, T> { + PinMut { inner: reference } } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized> Pin<'a, T> { - /// Construct a new `Pin` around a reference to some data of a type that +impl<'a, T: ?Sized> PinMut<'a, T> { + /// Construct a new `PinMut` around a reference to some data of a type that /// may or may not implement `Unpin`. /// /// This constructor is unsafe because we do not know what will happen with /// that data after the reference ends. If you cannot guarantee that the /// data will never move again, calling this constructor is invalid. #[unstable(feature = "pin", issue = "49150")] - pub unsafe fn new_unchecked(reference: &'a mut T) -> Pin<'a, T> { - Pin { inner: reference } + pub unsafe fn new_unchecked(reference: &'a mut T) -> PinMut<'a, T> { + PinMut { inner: reference } } - /// Borrow a Pin for a shorter lifetime than it already has. + /// Borrow a PinMut for a shorter lifetime than it already has. #[unstable(feature = "pin", issue = "49150")] - pub fn borrow<'b>(this: &'b mut Pin<'a, T>) -> Pin<'b, T> { - Pin { inner: this.inner } + pub fn borrow<'b>(this: &'b mut PinMut<'a, T>) -> PinMut<'b, T> { + PinMut { inner: this.inner } } - /// Get a mutable reference to the data inside of this `Pin`. + /// Get a mutable reference to the data inside of this `PinMut`. /// /// This function is unsafe. You must guarantee that you will never move /// the data out of the mutable reference you receive when you call this /// function. #[unstable(feature = "pin", issue = "49150")] - pub unsafe fn get_mut<'b>(this: &'b mut Pin<'a, T>) -> &'b mut T { + pub unsafe fn get_mut<'b>(this: &'b mut PinMut<'a, T>) -> &'b mut T { this.inner } /// Construct a new pin by mapping the interior value. /// - /// For example, if you wanted to get a `Pin` of a field of something, you + /// For example, if you wanted to get a `PinMut` of a field of something, you /// could use this to get access to that field in one line of code. /// /// This function is unsafe. You must guarantee that the data you return @@ -1155,15 +1155,15 @@ impl<'a, T: ?Sized> Pin<'a, T> { /// because it is one of the fields of that value), and also that you do /// not move out of the argument you receive to the interior function. #[unstable(feature = "pin", issue = "49150")] - pub unsafe fn map<'b, U, F>(this: &'b mut Pin<'a, T>, f: F) -> Pin<'b, U> where + pub unsafe fn map<'b, U, F>(this: &'b mut PinMut<'a, T>, f: F) -> PinMut<'b, U> where F: FnOnce(&mut T) -> &mut U { - Pin { inner: f(this.inner) } + PinMut { inner: f(this.inner) } } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized> Deref for Pin<'a, T> { +impl<'a, T: ?Sized> Deref for PinMut<'a, T> { type Target = T; fn deref(&self) -> &T { @@ -1172,35 +1172,35 @@ impl<'a, T: ?Sized> Deref for Pin<'a, T> { } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized + Unpin> DerefMut for Pin<'a, T> { +impl<'a, T: ?Sized + Unpin> DerefMut for PinMut<'a, T> { fn deref_mut(&mut self) -> &mut T { self.inner } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for Pin<'a, T> { +impl<'a, T: fmt::Debug + ?Sized> fmt::Debug for PinMut<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Debug::fmt(&**self, f) } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: fmt::Display + ?Sized> fmt::Display for Pin<'a, T> { +impl<'a, T: fmt::Display + ?Sized> fmt::Display for PinMut<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Display::fmt(&**self, f) } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized> fmt::Pointer for Pin<'a, T> { +impl<'a, T: ?Sized> fmt::Pointer for PinMut<'a, T> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { fmt::Pointer::fmt(&(&*self.inner as *const T), f) } } #[unstable(feature = "pin", issue = "49150")] -impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for Pin<'a, T> {} +impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for PinMut<'a, T> {} #[unstable(feature = "pin", issue = "49150")] -unsafe impl<'a, T: ?Sized> Unpin for Pin<'a, T> {} +unsafe impl<'a, T: ?Sized> Unpin for PinMut<'a, T> {} -- cgit 1.4.1-3-g733a5 From a72a0801bdab22d7c0167f67d1148276ea874d2e Mon Sep 17 00:00:00 2001 From: Manish Goregaokar Date: Mon, 7 May 2018 09:30:11 -0700 Subject: Add explanation for #[must_use] on string replace methods --- src/liballoc/str.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 0cbe65db53c..9e693c89be9 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -207,7 +207,8 @@ impl str { /// let s = "this is old"; /// assert_eq!(s, s.replace("cookie monster", "little lamb")); /// ``` - #[must_use] + #[must_use = "this returns the replaced string as a new allocation, \ + without modifying the original"] #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn replace<'a, P: Pattern<'a>>(&'a self, from: P, to: &str) -> String { @@ -247,7 +248,8 @@ impl str { /// let s = "this is old"; /// assert_eq!(s, s.replacen("cookie monster", "little lamb", 10)); /// ``` - #[must_use] + #[must_use = "this returns the replaced string as a new allocation, \ + without modifying the original"] #[stable(feature = "str_replacen", since = "1.16.0")] pub fn replacen<'a, P: Pattern<'a>>(&'a self, pat: P, to: &str, count: usize) -> String { // Hope to reduce the times of re-allocation -- cgit 1.4.1-3-g733a5 From 669bd8223bf159d757d0c552a4c413a137bc6b10 Mon Sep 17 00:00:00 2001 From: C Jones Date: Mon, 30 Apr 2018 15:24:59 -0400 Subject: Make LeafNode #[repr(C)] and put the metadata before generic items This way we can safely statically allocate a LeafNode to use as the placeholder before allocating, and any type accessing it will be able to access the metadata at the same offset. --- src/liballoc/btree/node.rs | 20 ++++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index d6346662314..3e27331aa38 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -60,12 +60,12 @@ pub const CAPACITY: usize = 2 * B - 1; /// /// See also rust-lang/rfcs#197, which would make this structure significantly more safe by /// avoiding accidentally dropping unused and uninitialized keys and values. +/// +/// We put the metadata first so that its position is the same for every `K` and `V`, in order +/// to statically allocate a single dummy node to avoid allocations. This struct is `repr(C)` to +/// prevent them from being reordered. +#[repr(C)] struct LeafNode { - /// The arrays storing the actual data of the node. Only the first `len` elements of each - /// array are initialized and valid. - keys: [K; CAPACITY], - vals: [V; CAPACITY], - /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. /// This either points to an actual node or is null. parent: *const InternalNode, @@ -77,10 +77,14 @@ struct LeafNode { /// The number of keys and values this node stores. /// - /// This is at the end of the node's representation and next to `parent_idx` to encourage - /// the compiler to join `len` and `parent_idx` into the same 32-bit word, reducing space - /// overhead. + /// This next to `parent_idx` to encourage the compiler to join `len` and + /// `parent_idx` into the same 32-bit word, reducing space overhead. len: u16, + + /// The arrays storing the actual data of the node. Only the first `len` elements of each + /// array are initialized and valid. + keys: [K; CAPACITY], + vals: [V; CAPACITY], } impl LeafNode { -- cgit 1.4.1-3-g733a5 From ef6060c863c86e1422baa2cc85ae75af22feaf51 Mon Sep 17 00:00:00 2001 From: C Jones Date: Mon, 30 Apr 2018 17:34:14 -0400 Subject: Add a statically allocated empty node for empty maps This gives a pointer to that static empty node instead of allocating a new node, and then whenever inserting makes sure that the root isn't that empty node. --- src/liballoc/btree/map.rs | 15 +++++++++++++-- src/liballoc/btree/node.rs | 30 ++++++++++++++++++++++++++++++ 2 files changed, 43 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 3984379ea86..985a41722d7 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -523,7 +523,7 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn new() -> BTreeMap { BTreeMap { - root: node::Root::new_leaf(), + root: node::Root::shared_empty_root(), length: 0, } } @@ -544,7 +544,6 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn clear(&mut self) { - // FIXME(gereeter) .clear() allocates *self = BTreeMap::new(); } @@ -687,6 +686,10 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, key: K, value: V) -> Option { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } + match self.entry(key) { Occupied(mut entry) => Some(entry.insert(value)), Vacant(entry) => { @@ -890,6 +893,10 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn entry(&mut self, key: K) -> Entry { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } + match search::search_tree(self.root.as_mut(), &key) { Found(handle) => { Occupied(OccupiedEntry { @@ -910,6 +917,10 @@ impl BTreeMap { } fn from_sorted_iter>(&mut self, iter: I) { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } + let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node(); // Iterate through all key-value pairs, pushing them into nodes at the right level. for (key, value) in iter { diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 3e27331aa38..6381006e7fc 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -103,6 +103,18 @@ impl LeafNode { } } +// We need to implement Sync here in order to make a static instance +unsafe impl Sync for LeafNode<(), ()> {} + +// An empty node used as a placeholder for the root node, to avoid allocations +static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode { + parent: ptr::null(), + parent_idx: 0, + len: 0, + keys: [(); CAPACITY], + vals: [(); CAPACITY], +}; + /// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden /// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an /// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the @@ -172,6 +184,24 @@ unsafe impl Sync for Root { } unsafe impl Send for Root { } impl Root { + pub fn is_shared_root(&self) -> bool { + ptr::eq( + self.node.as_ptr().as_ptr(), + &EMPTY_ROOT_NODE as *const _ as *const LeafNode, + ) + } + + pub fn shared_empty_root() -> Self { + Root { + node: unsafe { + BoxedNode::from_ptr(NonNull::new_unchecked( + &EMPTY_ROOT_NODE as *const _ as *const LeafNode as *mut _ + )) + }, + height: 0, + } + } + pub fn new_leaf() -> Self { Root { node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), -- cgit 1.4.1-3-g733a5 From fa62eba92ad9a3d25b200835a5cd3ca48b700d75 Mon Sep 17 00:00:00 2001 From: C Jones Date: Tue, 1 May 2018 00:21:30 -0400 Subject: Don't drop the shared static node We modify the drop implementation in IntoIter to not drop the shared root --- src/liballoc/btree/map.rs | 8 ++++---- src/liballoc/btree/node.rs | 13 +++++++++---- 2 files changed, 13 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 985a41722d7..a88631b1e67 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -686,10 +686,6 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn insert(&mut self, key: K, value: V) -> Option { - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } - match self.entry(key) { Occupied(mut entry) => Some(entry.insert(value)), Vacant(entry) => { @@ -1301,6 +1297,10 @@ impl Drop for IntoIter { self.for_each(drop); unsafe { let leaf_node = ptr::read(&self.front).into_node(); + if leaf_node.is_shared_root() { + return; + } + if let Some(first_parent) = leaf_node.deallocate_and_ascend() { let mut cur_node = first_parent.into_node(); while let Some(parent) = cur_node.deallocate_and_ascend() { diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 6381006e7fc..79615e11c67 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -101,6 +101,10 @@ impl LeafNode { len: 0 } } + + fn is_shared_root(&self) -> bool { + self as *const _ == &EMPTY_ROOT_NODE as *const _ as *const LeafNode + } } // We need to implement Sync here in order to make a static instance @@ -185,10 +189,7 @@ unsafe impl Send for Root { } impl Root { pub fn is_shared_root(&self) -> bool { - ptr::eq( - self.node.as_ptr().as_ptr(), - &EMPTY_ROOT_NODE as *const _ as *const LeafNode, - ) + self.as_ref().is_shared_root() } pub fn shared_empty_root() -> Self { @@ -387,6 +388,10 @@ impl NodeRef { } } + pub fn is_shared_root(&self) -> bool { + self.as_leaf().is_shared_root() + } + pub fn keys(&self) -> &[K] { self.reborrow().into_slices().0 } -- cgit 1.4.1-3-g733a5 From 5b94e9f053c3fecb0e29c89e453ecaf07d97218c Mon Sep 17 00:00:00 2001 From: C Jones Date: Tue, 1 May 2018 15:26:49 -0400 Subject: Split into_slices() to avoid making extra slices This splits into_slices() into into_key_slice() and into_val_slice(). While the extra calls would get optimized out, this is a useful semantic change since we call keys() while iterating, and we don't want to construct and out-of-bounds val() pointer in the process if we happen to be pointing to the shared static root. This also paves the way for doing the alignment handling conditional differently for the keys and values. --- src/liballoc/btree/node.rs | 66 ++++++++++++++++++++++++++++------------------ 1 file changed, 41 insertions(+), 25 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 79615e11c67..4dcc4d54eaf 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -393,11 +393,11 @@ impl NodeRef { } pub fn keys(&self) -> &[K] { - self.reborrow().into_slices().0 + self.reborrow().into_key_slice() } - pub fn vals(&self) -> &[V] { - self.reborrow().into_slices().1 + fn vals(&self) -> &[V] { + self.reborrow().into_val_slice() } /// Finds the parent of the current node. Returns `Ok(handle)` if the current @@ -540,29 +540,37 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { } pub fn keys_mut(&mut self) -> &mut [K] { - unsafe { self.reborrow_mut().into_slices_mut().0 } + unsafe { self.reborrow_mut().into_key_slice_mut() } } pub fn vals_mut(&mut self) -> &mut [V] { - unsafe { self.reborrow_mut().into_slices_mut().1 } + unsafe { self.reborrow_mut().into_val_slice_mut() } } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - pub fn into_slices(self) -> (&'a [K], &'a [V]) { + fn into_key_slice(self) -> &'a [K] { unsafe { - ( - slice::from_raw_parts( - self.as_leaf().keys.as_ptr(), - self.len() - ), - slice::from_raw_parts( - self.as_leaf().vals.as_ptr(), - self.len() - ) + slice::from_raw_parts( + self.as_leaf().keys.as_ptr(), + self.len() + ) + } + } + + fn into_val_slice(self) -> &'a [V] { + unsafe { + slice::from_raw_parts( + self.as_leaf().vals.as_ptr(), + self.len() ) } } + + fn into_slices(self) -> (&'a [K], &'a [V]) { + let k = unsafe { ptr::read(&self) }; + (k.into_key_slice(), self.into_val_slice()) + } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { @@ -574,20 +582,28 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } } - pub fn into_slices_mut(mut self) -> (&'a mut [K], &'a mut [V]) { + fn into_key_slice_mut(mut self) -> &'a mut [K] { unsafe { - ( - slice::from_raw_parts_mut( - &mut self.as_leaf_mut().keys as *mut [K] as *mut K, - self.len() - ), - slice::from_raw_parts_mut( - &mut self.as_leaf_mut().vals as *mut [V] as *mut V, - self.len() - ) + slice::from_raw_parts_mut( + &mut self.as_leaf_mut().keys as *mut [K] as *mut K, + self.len() + ) + } + } + + fn into_val_slice_mut(mut self) -> &'a mut [V] { + unsafe { + slice::from_raw_parts_mut( + &mut self.as_leaf_mut().vals as *mut [V] as *mut V, + self.len() ) } } + + fn into_slices_mut(self) -> (&'a mut [K], &'a mut [V]) { + let k = unsafe { ptr::read(&self) }; + (k.into_key_slice_mut(), self.into_val_slice_mut()) + } } impl<'a, K, V> NodeRef, K, V, marker::Leaf> { -- cgit 1.4.1-3-g733a5 From ddacf037fdc8bfb845bde2ce41ea4b9b6de445c7 Mon Sep 17 00:00:00 2001 From: C Jones Date: Tue, 1 May 2018 16:37:07 -0400 Subject: Make into_key_slice avoid taking out-of-bounds pointers --- src/liballoc/btree/node.rs | 48 ++++++++++++++++++++++++++++++++-------------- 1 file changed, 34 insertions(+), 14 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 4dcc4d54eaf..ce770d384f8 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -107,10 +107,12 @@ impl LeafNode { } } -// We need to implement Sync here in order to make a static instance +// We need to implement Sync here in order to make a static instance. unsafe impl Sync for LeafNode<(), ()> {} -// An empty node used as a placeholder for the root node, to avoid allocations +// An empty node used as a placeholder for the root node, to avoid allocations. +// We use () in order to save space, since no operation on an empty tree will +// ever take a pointer past the first key. static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode { parent: ptr::null(), parent_idx: 0, @@ -539,26 +541,39 @@ impl<'a, K, V, Type> NodeRef, K, V, Type> { } } - pub fn keys_mut(&mut self) -> &mut [K] { + fn keys_mut(&mut self) -> &mut [K] { unsafe { self.reborrow_mut().into_key_slice_mut() } } - pub fn vals_mut(&mut self) -> &mut [V] { + fn vals_mut(&mut self) -> &mut [V] { unsafe { self.reborrow_mut().into_val_slice_mut() } } } impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { fn into_key_slice(self) -> &'a [K] { - unsafe { - slice::from_raw_parts( - self.as_leaf().keys.as_ptr(), - self.len() - ) + // When taking a pointer to the keys, if our key has a stricter + // alignment requirement than the shared root does, then the pointer + // would be out of bounds, which LLVM assumes will not happen. If the + // alignment is more strict, we need to make an empty slice that doesn't + // use an out of bounds pointer. + if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + &[] + } else { + // Here either it's not the root, or the alignment is less strict, + // in which case the keys pointer will point "one-past-the-end" of + // the node, which is allowed by LLVM. + unsafe { + slice::from_raw_parts( + self.as_leaf().keys.as_ptr(), + self.len() + ) + } } } fn into_val_slice(self) -> &'a [V] { + debug_assert!(!self.is_shared_root()); unsafe { slice::from_raw_parts( self.as_leaf().vals.as_ptr(), @@ -583,15 +598,20 @@ impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { } fn into_key_slice_mut(mut self) -> &'a mut [K] { - unsafe { - slice::from_raw_parts_mut( - &mut self.as_leaf_mut().keys as *mut [K] as *mut K, - self.len() - ) + if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + &mut [] + } else { + unsafe { + slice::from_raw_parts_mut( + &mut self.as_leaf_mut().keys as *mut [K] as *mut K, + self.len() + ) + } } } fn into_val_slice_mut(mut self) -> &'a mut [V] { + debug_assert!(!self.is_shared_root()); unsafe { slice::from_raw_parts_mut( &mut self.as_leaf_mut().vals as *mut [V] as *mut V, -- cgit 1.4.1-3-g733a5 From f3a3599e090cb6aa63a327351738d7633c934728 Mon Sep 17 00:00:00 2001 From: C Jones Date: Mon, 7 May 2018 19:42:28 -0400 Subject: Add debug asserts and fix some violations --- src/liballoc/btree/map.rs | 5 +++++ src/liballoc/btree/node.rs | 11 +++++++++++ 2 files changed, 16 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index a88631b1e67..7ed7fd204fd 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -246,6 +246,9 @@ impl super::Recover for BTreeMap } fn replace(&mut self, key: K) -> Option { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } match search::search_tree::(self.root.as_mut(), &key) { Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), GoDown(handle) => { @@ -889,6 +892,7 @@ impl BTreeMap { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn entry(&mut self, key: K) -> Entry { + // FIXME(@porglezomp) Avoid allocating if we don't insert if self.root.is_shared_root() { self.root = node::Root::new_leaf(); } @@ -1026,6 +1030,7 @@ impl BTreeMap { let total_num = self.len(); let mut right = Self::new(); + right.root = node::Root::new_leaf(); for _ in 0..(self.root.as_ref().height()) { right.root.push_level(); } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index ce770d384f8..17eee65178e 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -195,6 +195,10 @@ impl Root { } pub fn shared_empty_root() -> Self { + // Ensuring that the shared node hasn't been corrupted by any mutations + debug_assert!(EMPTY_ROOT_NODE.parent == ptr::null()); + debug_assert!(EMPTY_ROOT_NODE.parent_idx == 0); + debug_assert!(EMPTY_ROOT_NODE.len == 0); Root { node: unsafe { BoxedNode::from_ptr(NonNull::new_unchecked( @@ -246,6 +250,7 @@ impl Root { /// new node the root. This increases the height by 1 and is the opposite of `pop_level`. pub fn push_level(&mut self) -> NodeRef { + debug_assert!(!self.is_shared_root()); let mut new_node = Box::new(unsafe { InternalNode::new() }); new_node.edges[0] = unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }; @@ -474,6 +479,7 @@ impl NodeRef { marker::Edge > > { + debug_assert!(!self.is_shared_root()); let node = self.node; let ret = self.ascend().ok(); Global.dealloc(node.as_opaque(), Layout::new::>()); @@ -631,6 +637,7 @@ impl<'a, K, V> NodeRef, K, V, marker::Leaf> { pub fn push(&mut self, key: K, val: V) { // Necessary for correctness, but this is an internal module debug_assert!(self.len() < CAPACITY); + debug_assert!(!self.is_shared_root()); let idx = self.len(); @@ -646,6 +653,7 @@ impl<'a, K, V> NodeRef, K, V, marker::Leaf> { pub fn push_front(&mut self, key: K, val: V) { // Necessary for correctness, but this is an internal module debug_assert!(self.len() < CAPACITY); + debug_assert!(!self.is_shared_root()); unsafe { slice_insert(self.keys_mut(), 0, key); @@ -959,6 +967,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge fn insert_fit(&mut self, key: K, val: V) -> *mut V { // Necessary for correctness, but in a private module debug_assert!(self.node.len() < CAPACITY); + debug_assert!(!self.node.is_shared_root()); unsafe { slice_insert(self.node.keys_mut(), self.idx, key); @@ -1136,6 +1145,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> /// allocated node. pub fn split(mut self) -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { + debug_assert!(!self.node.is_shared_root()); unsafe { let mut new_node = Box::new(LeafNode::new()); @@ -1173,6 +1183,7 @@ impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> /// now adjacent key/value pairs to the left and right of this handle. pub fn remove(mut self) -> (Handle, K, V, marker::Leaf>, marker::Edge>, K, V) { + debug_assert!(!self.node.is_shared_root()); unsafe { let k = slice_remove(self.node.keys_mut(), self.idx); let v = slice_remove(self.node.vals_mut(), self.idx); -- cgit 1.4.1-3-g733a5 From 663c0961b9e72301ab5400f3aa066acf20586268 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 8 May 2018 17:07:24 +0900 Subject: Cleanup a `use` in a raw_vec test `allocator` is deprecated in favor of `alloc`, and `Alloc` is already imported through `super::*`. --- src/liballoc/raw_vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index eb25ae17511..4d73d3aa07e 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -748,7 +748,7 @@ mod tests { #[test] fn allocator_param() { - use allocator::{Alloc, AllocErr}; + use alloc::AllocErr; // Writing a test of integration between third-party // allocators and RawVec is a little tricky because the RawVec -- cgit 1.4.1-3-g733a5 From e83c18f91d373592ecf7a0fbbc24d7597925af13 Mon Sep 17 00:00:00 2001 From: C Jones Date: Tue, 8 May 2018 13:28:49 -0400 Subject: Make an ensure_root_is_owned method to reduce duplication Also remove some unnecessary debug_assert! when creating the shared root, since the root should be stored in the rodata and thus be impossible to accidentally modify. --- src/liballoc/btree/map.rs | 21 ++++++++++----------- src/liballoc/btree/node.rs | 4 ---- 2 files changed, 10 insertions(+), 15 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 7ed7fd204fd..bb2c68a27ba 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -246,9 +246,7 @@ impl super::Recover for BTreeMap } fn replace(&mut self, key: K) -> Option { - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } + self.ensure_root_is_owned(); match search::search_tree::(self.root.as_mut(), &key) { Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), GoDown(handle) => { @@ -893,10 +891,7 @@ impl BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] pub fn entry(&mut self, key: K) -> Entry { // FIXME(@porglezomp) Avoid allocating if we don't insert - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } - + self.ensure_root_is_owned(); match search::search_tree(self.root.as_mut(), &key) { Found(handle) => { Occupied(OccupiedEntry { @@ -917,10 +912,7 @@ impl BTreeMap { } fn from_sorted_iter>(&mut self, iter: I) { - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } - + self.ensure_root_is_owned(); let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node(); // Iterate through all key-value pairs, pushing them into nodes at the right level. for (key, value) in iter { @@ -1165,6 +1157,13 @@ impl BTreeMap { self.fix_top(); } + + /// If the root node is the shared root node, allocate our own node. + fn ensure_root_is_owned(&mut self) { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } + } } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 17eee65178e..431695c32ab 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -195,10 +195,6 @@ impl Root { } pub fn shared_empty_root() -> Self { - // Ensuring that the shared node hasn't been corrupted by any mutations - debug_assert!(EMPTY_ROOT_NODE.parent == ptr::null()); - debug_assert!(EMPTY_ROOT_NODE.parent_idx == 0); - debug_assert!(EMPTY_ROOT_NODE.len == 0); Root { node: unsafe { BoxedNode::from_ptr(NonNull::new_unchecked( -- cgit 1.4.1-3-g733a5 From 17e262880c13abdc4d7bf72eb7e87146f0f3b1f0 Mon Sep 17 00:00:00 2001 From: George Burton Date: Wed, 9 May 2018 07:23:02 +0100 Subject: Update features to 1.28.0 --- src/liballoc/string.rs | 2 +- src/liballoc/vec.rs | 2 +- src/libstd/ffi/c_str.rs | 8 ++++---- src/libstd/ffi/os_str.rs | 8 ++++---- src/libstd/path.rs | 4 ++-- 5 files changed, 12 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 61a207fc5b3..5f684361fdc 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -2239,7 +2239,7 @@ impl<'a> From for Cow<'a, str> { } } -#[stable(feature = "cow_from_string_ref", since = "1.27.0")] +#[stable(feature = "cow_from_string_ref", since = "1.28.0")] impl<'a> From<&'a String> for Cow<'a, str> { #[inline] fn from(s: &'a String) -> Cow<'a, str> { diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 315a8a0aad0..be4c80c85d9 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2285,7 +2285,7 @@ impl<'a, T: Clone> From> for Cow<'a, [T]> { } } -#[stable(feature = "cow_from_vec_ref", since = "1.27.0")] +#[stable(feature = "cow_from_vec_ref", since = "1.28.0")] impl<'a, T: Clone> From<&'a Vec> for Cow<'a, [T]> { fn from(v: &'a Vec) -> Cow<'a, [T]> { Cow::Borrowed(v.as_slice()) diff --git a/src/libstd/ffi/c_str.rs b/src/libstd/ffi/c_str.rs index 10f59b0a3cc..05ab4c3cecc 100644 --- a/src/libstd/ffi/c_str.rs +++ b/src/libstd/ffi/c_str.rs @@ -682,7 +682,7 @@ impl Borrow for CString { fn borrow(&self) -> &CStr { self } } -#[stable(feature = "cstring_from_cow_cstr", since = "1.27.0")] +#[stable(feature = "cstring_from_cow_cstr", since = "1.2780")] impl<'a> From> for CString { #[inline] fn from(s: Cow<'a, CStr>) -> Self { @@ -714,7 +714,7 @@ impl From for Box { } } -#[stable(feature = "cow_from_cstr", since = "1.27.0")] +#[stable(feature = "cow_from_cstr", since = "1.28.0")] impl<'a> From for Cow<'a, CStr> { #[inline] fn from(s: CString) -> Cow<'a, CStr> { @@ -722,7 +722,7 @@ impl<'a> From for Cow<'a, CStr> { } } -#[stable(feature = "cow_from_cstr", since = "1.27.0")] +#[stable(feature = "cow_from_cstr", since = "1.28.0")] impl<'a> From<&'a CStr> for Cow<'a, CStr> { #[inline] fn from(s: &'a CStr) -> Cow<'a, CStr> { @@ -730,7 +730,7 @@ impl<'a> From<&'a CStr> for Cow<'a, CStr> { } } -#[stable(feature = "cow_from_cstr", since = "1.27.0")] +#[stable(feature = "cow_from_cstr", since = "1.28.0")] impl<'a> From<&'a CString> for Cow<'a, CStr> { #[inline] fn from(s: &'a CString) -> Cow<'a, CStr> { diff --git a/src/libstd/ffi/os_str.rs b/src/libstd/ffi/os_str.rs index d865ffa8e2f..0a3148029d0 100644 --- a/src/libstd/ffi/os_str.rs +++ b/src/libstd/ffi/os_str.rs @@ -664,7 +664,7 @@ impl<'a> From<&'a OsStr> for Rc { } } -#[stable(feature = "cow_from_osstr", since = "1.27.0")] +#[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From for Cow<'a, OsStr> { #[inline] fn from(s: OsString) -> Cow<'a, OsStr> { @@ -672,7 +672,7 @@ impl<'a> From for Cow<'a, OsStr> { } } -#[stable(feature = "cow_from_osstr", since = "1.27.0")] +#[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { #[inline] fn from(s: &'a OsStr) -> Cow<'a, OsStr> { @@ -680,7 +680,7 @@ impl<'a> From<&'a OsStr> for Cow<'a, OsStr> { } } -#[stable(feature = "cow_from_osstr", since = "1.27.0")] +#[stable(feature = "cow_from_osstr", since = "1.28.0")] impl<'a> From<&'a OsString> for Cow<'a, OsStr> { #[inline] fn from(s: &'a OsString) -> Cow<'a, OsStr> { @@ -688,7 +688,7 @@ impl<'a> From<&'a OsString> for Cow<'a, OsStr> { } } -#[stable(feature = "osstring_from_cow_osstr", since = "1.27.0")] +#[stable(feature = "osstring_from_cow_osstr", since = "1.28.0")] impl<'a> From> for OsString { #[inline] fn from(s: Cow<'a, OsStr>) -> Self { diff --git a/src/libstd/path.rs b/src/libstd/path.rs index 83633210ff2..b7ab14b29ca 100644 --- a/src/libstd/path.rs +++ b/src/libstd/path.rs @@ -1532,7 +1532,7 @@ impl<'a> From for Cow<'a, Path> { } } -#[stable(feature = "cow_from_pathbuf_ref", since = "1.27.0")] +#[stable(feature = "cow_from_pathbuf_ref", since = "1.28.0")] impl<'a> From<&'a PathBuf> for Cow<'a, Path> { #[inline] fn from(p: &'a PathBuf) -> Cow<'a, Path> { @@ -1540,7 +1540,7 @@ impl<'a> From<&'a PathBuf> for Cow<'a, Path> { } } -#[stable(feature = "pathbuf_from_cow_path", since = "1.27.0")] +#[stable(feature = "pathbuf_from_cow_path", since = "1.28.0")] impl<'a> From> for PathBuf { #[inline] fn from(p: Cow<'a, Path>) -> Self { -- cgit 1.4.1-3-g733a5 From 254b6014d20f51a3e91b88c24a8f19e31f17acc9 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Wed, 9 May 2018 08:33:49 -0700 Subject: std: Avoid `ptr::copy` if unnecessary in `vec::Drain` This commit is spawned out of a performance regression investigation in #50496. In tracking down this regression it turned out that the `expand_statements` function in the compiler was taking quite a long time. Further investigation showed two key properties: * The function was "fast" on glibc 2.24 and slow on glibc 2.23 * The hottest function was memmove from glibc Combined together it looked like glibc gained an optimization to the memmove function in 2.24. Ideally we don't want to rely on this optimization, so I wanted to dig further to see what was happening. The hottest part of `expand_statements` was `Drop for Drain` in the call to `splice` where we insert new statements into the original vector. This *should* be a cheap operation because we're draining and replacing iterators of the exact same length, but under the hood memmove was being called a lot, causing a slowdown on glibc 2.23. It turns out that at least one of the optimizations in glibc 2.24 was that `memmove` where the src/dst are equal becomes much faster. [This program][prog] executes in ~2.5s against glibc 2.23 and ~0.3s against glibc 2.24, exhibiting how glibc 2.24 is optimizing `memmove` if the src/dst are equal. And all that brings us to what this commit itself is doing. The change here is purely to `Drop for Drain` to avoid the call to `ptr::copy` if the region being copied doesn't actually need to be copied. For normal usage of just `Drain` itself this check isn't really necessary, but because `Splice` internally contains `Drain` this provides a nice speed boost on glibc 2.23. Overall this should fix the regression seen in #50496 on glibc 2.23 and also fix the regression on Windows where `memmove` looks to not have this optimization. Note that the way `splice` was called in `expand_statements` would cause a quadratic number of elements to be copied via `memmove` which is likely why the tuple-stress benchmark showed such a severe regression. Closes #50496 [prog]: https://gist.github.com/alexcrichton/c05bc51c6771bba5ae5b57561a6c1cd3 --- src/liballoc/vec.rs | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 35d0a69a05a..690cbcb559b 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -2533,9 +2533,11 @@ impl<'a, T> Drop for Drain<'a, T> { // memmove back untouched tail, update to new length let start = source_vec.len(); let tail = self.tail_start; - let src = source_vec.as_ptr().offset(tail as isize); - let dst = source_vec.as_mut_ptr().offset(start as isize); - ptr::copy(src, dst, self.tail_len); + if tail != start { + let src = source_vec.as_ptr().offset(tail as isize); + let dst = source_vec.as_mut_ptr().offset(start as isize); + ptr::copy(src, dst, self.tail_len); + } source_vec.set_len(start + self.tail_len); } } -- cgit 1.4.1-3-g733a5 From 8010604b2d888ac839147fe27de76cdcc713aa1b Mon Sep 17 00:00:00 2001 From: Michael Lamparski Date: Wed, 9 May 2018 18:03:56 -0400 Subject: move See also links to top --- src/liballoc/slice.rs | 4 ++-- src/liballoc/str.rs | 4 ++-- src/libcore/num/f32.rs | 4 ++-- src/libcore/num/f64.rs | 4 ++-- src/libstd/f32.rs | 4 ++-- src/libstd/f64.rs | 4 ++-- src/libstd/primitive_docs.rs | 16 ++++++++-------- 7 files changed, 20 insertions(+), 20 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index d50a3458f20..6caf12aa7eb 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -10,6 +10,8 @@ //! A dynamically-sized view into a contiguous sequence, `[T]`. //! +//! *[See also the slice primitive type](../../std/primitive.slice.html).* +//! //! Slices are a view into a block of memory represented as a pointer and a //! length. //! @@ -78,8 +80,6 @@ //! * Further methods that return iterators are [`.split`], [`.splitn`], //! [`.chunks`], [`.windows`] and more. //! -//! *[See also the slice primitive type](../../std/primitive.slice.html).* -//! //! [`Clone`]: ../../std/clone/trait.Clone.html //! [`Eq`]: ../../std/cmp/trait.Eq.html //! [`Ord`]: ../../std/cmp/trait.Ord.html diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 9e693c89be9..42efdea74b1 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -10,6 +10,8 @@ //! Unicode string slices. //! +//! *[See also the `str` primitive type](../../std/primitive.str.html).* +//! //! The `&str` type is one of the two main string types, the other being `String`. //! Unlike its `String` counterpart, its contents are borrowed. //! @@ -29,8 +31,6 @@ //! ``` //! let hello_world: &'static str = "Hello, world!"; //! ``` -//! -//! *[See also the `str` primitive type](../../std/primitive.str.html).* #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 672119eba7f..4a7dc13f0f2 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 220b23a1e6a..801de5e87bd 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 26644c76957..f4d897b0111 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f32` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f32` primitive type](../../std/primitive.f32.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index a7e63f59b1c..bd24e84dbed 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -11,9 +11,9 @@ //! This module provides constants which are specific to the implementation //! of the `f64` floating point data type. //! -//! Mathematically significant numbers are provided in the `consts` sub-module. -//! //! *[See also the `f64` primitive type](../../std/primitive.f64.html).* +//! +//! Mathematically significant numbers are provided in the `consts` sub-module. #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] diff --git a/src/libstd/primitive_docs.rs b/src/libstd/primitive_docs.rs index 437d7d74cae..6e329d85539 100644 --- a/src/libstd/primitive_docs.rs +++ b/src/libstd/primitive_docs.rs @@ -370,6 +370,8 @@ mod prim_unit { } // /// Raw, unsafe pointers, `*const T`, and `*mut T`. /// +/// *[See also the `std::ptr` module](ptr/index.html).* +/// /// Working with raw pointers in Rust is uncommon, /// typically limited to a few patterns. /// @@ -444,8 +446,6 @@ mod prim_unit { } /// but C APIs hand out a lot of pointers generally, so are a common source /// of raw pointers in Rust. /// -/// *[See also the `std::ptr` module](ptr/index.html).* -/// /// [`null`]: ../std/ptr/fn.null.html /// [`null_mut`]: ../std/ptr/fn.null_mut.html /// [`is_null`]: ../std/primitive.pointer.html#method.is_null @@ -563,6 +563,8 @@ mod prim_array { } // /// A dynamically-sized view into a contiguous sequence, `[T]`. /// +/// *[See also the `std::slice` module](slice/index.html).* +/// /// Slices are a view into a block of memory represented as a pointer and a /// length. /// @@ -585,8 +587,6 @@ mod prim_array { } /// assert_eq!(x, &[1, 7, 3]); /// ``` /// -/// *[See also the `std::slice` module](slice/index.html).* -/// #[stable(feature = "rust1", since = "1.0.0")] mod prim_slice { } @@ -862,11 +862,11 @@ mod prim_u128 { } // /// The pointer-sized signed integer type. /// +/// *[See also the `std::isize` module](isize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::isize` module](isize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_isize { } @@ -874,11 +874,11 @@ mod prim_isize { } // /// The pointer-sized unsigned integer type. /// +/// *[See also the `std::usize` module](usize/index.html).* +/// /// The size of this primitive is how many bytes it takes to reference any /// location in memory. For example, on a 32 bit target, this is 4 bytes /// and on a 64 bit target, this is 8 bytes. -/// -/// *[See also the `std::usize` module](usize/index.html).* #[stable(feature = "rust1", since = "1.0.0")] mod prim_usize { } -- cgit 1.4.1-3-g733a5 From 9c4e5b3b6cbf9b31ac7bd74d760214e36e79bfff Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 10 May 2018 09:16:10 +0900 Subject: Restore RawVec::reserve* documentation When the RawVec::try_reserve* methods were added, they took the place of the ::reserve* methods in the source file, and new ::reserve* methods wrapping the new try_reserve* methods were created. But the documentation didn't move along, such that: - reserve_* methods are barely documented. - try_reserve_* methods have unmodified documentation from reserve_*, such that their documentation indicate they are panicking/aborting. This moves the documentation back to the right methods, with a placeholder documentation for the try_reserve* methods. --- src/liballoc/raw_vec.rs | 113 ++++++++++++++++++++++++------------------------ 1 file changed, 57 insertions(+), 56 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 4d73d3aa07e..5c6f6b22aae 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -385,26 +385,7 @@ impl RawVec { } } - /// Ensures that the buffer contains at least enough space to hold - /// `used_cap + needed_extra_cap` elements. If it doesn't already, - /// will reallocate the minimum possible amount of memory necessary. - /// Generally this will be exactly the amount of memory necessary, - /// but in principle the allocator is free to give back more than - /// we asked for. - /// - /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate - /// the requested space. This is not really unsafe, but the unsafe - /// code *you* write that relies on the behavior of this function may break. - /// - /// # Panics - /// - /// * Panics if the requested capacity exceeds `usize::MAX` bytes. - /// * Panics on 32-bit platforms if the requested capacity exceeds - /// `isize::MAX` bytes. - /// - /// # Aborts - /// - /// Aborts on OOM + /// The same as `reserve_exact`, but returns on errors instead of panicking or aborting. pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { @@ -441,6 +422,26 @@ impl RawVec { } } + /// Ensures that the buffer contains at least enough space to hold + /// `used_cap + needed_extra_cap` elements. If it doesn't already, + /// will reallocate the minimum possible amount of memory necessary. + /// Generally this will be exactly the amount of memory necessary, + /// but in principle the allocator is free to give back more than + /// we asked for. + /// + /// If `used_cap` exceeds `self.cap()`, this may fail to actually allocate + /// the requested space. This is not really unsafe, but the unsafe + /// code *you* write that relies on the behavior of this function may break. + /// + /// # Panics + /// + /// * Panics if the requested capacity exceeds `usize::MAX` bytes. + /// * Panics on 32-bit platforms if the requested capacity exceeds + /// `isize::MAX` bytes. + /// + /// # Aborts + /// + /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve_exact(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), @@ -463,6 +464,42 @@ impl RawVec { Ok(cmp::max(double_cap, required_cap)) } + /// The same as `reserve`, but returns on errors instead of panicking or aborting. + pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) + -> Result<(), CollectionAllocErr> { + unsafe { + // NOTE: we don't early branch on ZSTs here because we want this + // to actually catch "asking for more than usize::MAX" in that case. + // If we make it past the first branch then we are guaranteed to + // panic. + + // Don't actually need any more capacity. + // Wrapping in case they give a bad `used_cap` + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { + return Ok(()); + } + + let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + + // FIXME: may crash and burn on over-reserve + alloc_guard(new_layout.size())?; + + let res = match self.current_layout() { + Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + } + None => self.a.alloc(new_layout), + }; + + self.ptr = res?.cast().into(); + self.cap = new_cap; + + Ok(()) + } + } + /// Ensures that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate enough space plus comfortable slack @@ -515,42 +552,6 @@ impl RawVec { /// # vector.push_all(&[1, 3, 5, 7, 9]); /// # } /// ``` - pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) - -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } - } - - /// The same as try_reserve, but errors are lowered to a call to oom(). pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { match self.try_reserve(used_cap, needed_extra_cap) { Err(CapacityOverflow) => capacity_overflow(), -- cgit 1.4.1-3-g733a5 From 2c5d13dc9ca6897ef65de99107e08cc4de5b11dc Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 10 May 2018 13:51:51 -0700 Subject: Skip a memory-hungry test that OOMs Attempting to fix https://travis-ci.org/rust-lang/rust/jobs/377407894 via some selective ignoring tests --- src/liballoc/tests/str.rs | 1 + 1 file changed, 1 insertion(+) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 2edd41a70b9..1a47e5433ea 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -479,6 +479,7 @@ mod slice_index { } #[test] + #[cfg(not(target_arch = "asmjs"))] // hits an OOM fn simple_big() { fn a_million_letter_x() -> String { let mut i = 0; -- cgit 1.4.1-3-g733a5 From 50c4506329673d38b3170f048f546a5885dd8310 Mon Sep 17 00:00:00 2001 From: gnzlbg Date: Mon, 14 May 2018 13:58:28 +0200 Subject: Switch Vec from doubling size on growth to using RawVec's reserve On growth, Vec does not require to exactly double its size for correctness, like, for example, VecDeque does. Using reserve instead better expresses this intent. It also allows to reuse Excess capacity on growth and for better growth-policies to be provided by RawVec. --- src/liballoc/vec.rs | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 690cbcb559b..ffaff20bcc9 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -840,7 +840,7 @@ impl Vec { // space for the new element if len == self.buf.cap() { - self.buf.double(); + self.reserve(1); } unsafe { @@ -1060,7 +1060,7 @@ impl Vec { // This will panic or abort if we would allocate > isize::MAX bytes // or if the length increment would overflow for zero-sized types. if self.len == self.buf.cap() { - self.buf.double(); + self.reserve(1); } unsafe { let end = self.as_mut_ptr().offset(self.len as isize); -- cgit 1.4.1-3-g733a5 From 89d9ca9b50e01cbc5dc78a26f15cc8c435bbc5a4 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 16 May 2018 18:07:35 +0200 Subject: Stabilize num::NonZeroU* Tracking issue: https://github.com/rust-lang/rust/issues/49137 --- src/liballoc/lib.rs | 1 - src/libcore/num/mod.rs | 16 +++++++--------- src/libcore/tests/lib.rs | 1 - src/librustc/lib.rs | 1 - src/librustc_data_structures/lib.rs | 1 - src/librustc_mir/lib.rs | 1 - src/libstd/lib.rs | 1 - src/libstd/num.rs | 3 +-- src/test/run-pass/ctfe/tuple-struct-constructors.rs | 1 - src/test/run-pass/enum-null-pointer-opt.rs | 2 -- src/test/ui/print_type_sizes/niche-filling.rs | 1 - 11 files changed, 8 insertions(+), 21 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index bb78c14b905..f7dd9d4f010 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -102,7 +102,6 @@ #![feature(lang_items)] #![feature(libc)] #![feature(needs_allocator)] -#![feature(nonzero)] #![feature(optin_builtin_traits)] #![feature(pattern)] #![feature(pin)] diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 342f57b50f0..ae8b3087240 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -21,9 +21,9 @@ use ops; use str::FromStr; macro_rules! impl_nonzero_fmt { - ( #[$stability: meta] ( $( $Trait: ident ),+ ) for $Ty: ident ) => { + ( ( $( $Trait: ident ),+ ) for $Ty: ident ) => { $( - #[$stability] + #[stable(feature = "nonzero", since = "1.28.0")] impl fmt::$Trait for $Ty { #[inline] fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { @@ -35,7 +35,7 @@ macro_rules! impl_nonzero_fmt { } macro_rules! nonzero_integers { - ( #[$stability: meta] $( $Ty: ident($Int: ty); )+ ) => { + ( $( $Ty: ident($Int: ty); )+ ) => { $( /// An integer that is known not to equal zero. /// @@ -46,7 +46,7 @@ macro_rules! nonzero_integers { /// use std::mem::size_of; /// assert_eq!(size_of::>(), size_of::()); /// ``` - #[$stability] + #[stable(feature = "nonzero", since = "1.28.0")] #[derive(Copy, Clone, Eq, PartialEq, Ord, PartialOrd, Hash)] pub struct $Ty(NonZero<$Int>); @@ -56,14 +56,14 @@ macro_rules! nonzero_integers { /// # Safety /// /// The value must not be zero. - #[$stability] + #[stable(feature = "nonzero", since = "1.28.0")] #[inline] pub const unsafe fn new_unchecked(n: $Int) -> Self { $Ty(NonZero(n)) } /// Create a non-zero if the given value is not zero. - #[$stability] + #[stable(feature = "nonzero", since = "1.28.0")] #[inline] pub fn new(n: $Int) -> Option { if n != 0 { @@ -74,7 +74,7 @@ macro_rules! nonzero_integers { } /// Returns the value as a primitive type. - #[$stability] + #[stable(feature = "nonzero", since = "1.28.0")] #[inline] pub fn get(self) -> $Int { self.0 .0 @@ -83,7 +83,6 @@ macro_rules! nonzero_integers { } impl_nonzero_fmt! { - #[$stability] (Debug, Display, Binary, Octal, LowerHex, UpperHex) for $Ty } )+ @@ -91,7 +90,6 @@ macro_rules! nonzero_integers { } nonzero_integers! { - #[unstable(feature = "nonzero", issue = "49137")] NonZeroU8(u8); NonZeroU16(u16); NonZeroU32(u32); diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 5e98e40e0d5..7fb4b503c01 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -26,7 +26,6 @@ #![feature(iterator_step_by)] #![feature(iterator_flatten)] #![feature(iterator_repeat_with)] -#![feature(nonzero)] #![feature(pattern)] #![feature(range_is_empty)] #![feature(raw)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 26ac9d6ee9e..ac6ff6831ad 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -56,7 +56,6 @@ #![feature(never_type)] #![feature(exhaustive_patterns)] #![feature(non_exhaustive)] -#![feature(nonzero)] #![feature(proc_macro_internals)] #![feature(quote)] #![feature(optin_builtin_traits)] diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index b2e7450e76c..328ff76c1cd 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -21,7 +21,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(collections_range)] -#![feature(nonzero)] #![feature(unboxed_closures)] #![feature(fn_traits)] #![feature(unsize)] diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index fbc0facbc49..ecced1b8168 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -30,7 +30,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(exhaustive_patterns)] #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] -#![feature(nonzero)] #![feature(inclusive_range_methods)] #![feature(crate_visibility_modifier)] #![feature(never_type)] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index d41739ab02c..9cdc6a21622 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -277,7 +277,6 @@ #![feature(needs_panic_runtime)] #![feature(never_type)] #![feature(exhaustive_patterns)] -#![feature(nonzero)] #![feature(num_bits_bytes)] #![feature(old_wrapping)] #![feature(on_unimplemented)] diff --git a/src/libstd/num.rs b/src/libstd/num.rs index aa806b947b0..3f90c1fa3b1 100644 --- a/src/libstd/num.rs +++ b/src/libstd/num.rs @@ -21,8 +21,7 @@ pub use core::num::{FpCategory, ParseIntError, ParseFloatError, TryFromIntError} #[stable(feature = "rust1", since = "1.0.0")] pub use core::num::Wrapping; -#[unstable(feature = "nonzero", issue = "49137")] -#[allow(deprecated)] +#[stable(feature = "nonzero", since = "1.28.0")] pub use core::num::{NonZeroU8, NonZeroU16, NonZeroU32, NonZeroU64, NonZeroU128, NonZeroUsize}; #[cfg(test)] use fmt; diff --git a/src/test/run-pass/ctfe/tuple-struct-constructors.rs b/src/test/run-pass/ctfe/tuple-struct-constructors.rs index ee1ce192fe0..d5f3e88fd52 100644 --- a/src/test/run-pass/ctfe/tuple-struct-constructors.rs +++ b/src/test/run-pass/ctfe/tuple-struct-constructors.rs @@ -10,7 +10,6 @@ // https://github.com/rust-lang/rust/issues/41898 -#![feature(nonzero)] use std::num::NonZeroU64; fn main() { diff --git a/src/test/run-pass/enum-null-pointer-opt.rs b/src/test/run-pass/enum-null-pointer-opt.rs index 12f17a1575e..34ed589d418 100644 --- a/src/test/run-pass/enum-null-pointer-opt.rs +++ b/src/test/run-pass/enum-null-pointer-opt.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(nonzero, core)] - use std::mem::size_of; use std::num::NonZeroUsize; use std::ptr::NonNull; diff --git a/src/test/ui/print_type_sizes/niche-filling.rs b/src/test/ui/print_type_sizes/niche-filling.rs index 1aad0b760b1..17e7a21cd02 100644 --- a/src/test/ui/print_type_sizes/niche-filling.rs +++ b/src/test/ui/print_type_sizes/niche-filling.rs @@ -22,7 +22,6 @@ // padding and overall computed sizes can be quite different. #![feature(start)] -#![feature(nonzero)] #![allow(dead_code)] use std::num::NonZeroU32; -- cgit 1.4.1-3-g733a5 From b63d7e2b1c4019e40051036bcb1fd5f254a8f6e2 Mon Sep 17 00:00:00 2001 From: Irina Popa Date: Tue, 8 May 2018 16:10:16 +0300 Subject: Rename trans to codegen everywhere. --- src/Cargo.lock | 94 +- src/Cargo.toml | 2 +- src/bootstrap/builder.rs | 2 +- src/bootstrap/check.rs | 10 +- src/bootstrap/compile.rs | 16 +- src/doc/rustc-ux-guidelines.md | 2 +- src/liballoc/alloc.rs | 2 +- src/libcore/intrinsics.rs | 2 +- src/librustc/Cargo.toml | 6 +- src/librustc/cfg/construct.rs | 6 +- src/librustc/dep_graph/dep_node.rs | 6 +- src/librustc/dep_graph/graph.rs | 4 +- src/librustc/hir/check_attr.rs | 2 +- src/librustc/hir/mod.rs | 17 +- src/librustc/ich/impls_hir.rs | 6 +- src/librustc/ich/mod.rs | 6 +- src/librustc/infer/anon_types/mod.rs | 2 +- src/librustc/lint/context.rs | 8 +- src/librustc/lint/mod.rs | 6 +- src/librustc/middle/dependency_format.rs | 2 +- src/librustc/middle/mem_categorization.rs | 4 +- src/librustc/middle/reachable.rs | 18 +- src/librustc/middle/region.rs | 2 +- src/librustc/mir/interpret/mod.rs | 2 +- src/librustc/mir/interpret/value.rs | 2 +- src/librustc/mir/mod.rs | 4 +- src/librustc/mir/mono.rs | 9 +- src/librustc/session/config.rs | 32 +- src/librustc/session/mod.rs | 12 +- src/librustc/traits/codegen/mod.rs | 184 ++ src/librustc/traits/mod.rs | 10 +- src/librustc/traits/project.rs | 6 +- src/librustc/traits/query/normalize.rs | 2 +- .../traits/query/normalize_erasing_regions.rs | 2 +- src/librustc/traits/structural_impls.rs | 2 +- src/librustc/traits/trans/mod.rs | 184 -- src/librustc/ty/adjustment.rs | 4 +- src/librustc/ty/cast.rs | 2 +- src/librustc/ty/context.rs | 4 +- src/librustc/ty/erase_regions.rs | 3 +- src/librustc/ty/fold.rs | 2 +- src/librustc/ty/instance.rs | 16 +- src/librustc/ty/layout.rs | 6 +- src/librustc/ty/maps/config.rs | 8 +- src/librustc/ty/maps/mod.rs | 22 +- src/librustc/ty/maps/on_disk_cache.rs | 4 +- src/librustc/ty/maps/plumbing.rs | 12 +- src/librustc/ty/mod.rs | 8 +- src/librustc/ty/sty.rs | 6 +- src/librustc/util/ppaux.rs | 4 +- src/librustc_borrowck/borrowck/README.md | 2 +- src/librustc_codegen_llvm/Cargo.toml | 49 + src/librustc_codegen_llvm/README.md | 7 + src/librustc_codegen_llvm/abi.rs | 696 ++++++ src/librustc_codegen_llvm/allocator.rs | 103 + src/librustc_codegen_llvm/asm.rs | 127 ++ src/librustc_codegen_llvm/attributes.rs | 259 +++ src/librustc_codegen_llvm/back/archive.rs | 325 +++ src/librustc_codegen_llvm/back/bytecode.rs | 160 ++ src/librustc_codegen_llvm/back/command.rs | 175 ++ src/librustc_codegen_llvm/back/link.rs | 1630 +++++++++++++ src/librustc_codegen_llvm/back/linker.rs | 1037 +++++++++ src/librustc_codegen_llvm/back/lto.rs | 773 +++++++ src/librustc_codegen_llvm/back/rpath.rs | 282 +++ src/librustc_codegen_llvm/back/symbol_export.rs | 396 ++++ src/librustc_codegen_llvm/back/wasm.rs | 261 +++ src/librustc_codegen_llvm/back/write.rs | 2390 +++++++++++++++++++ src/librustc_codegen_llvm/base.rs | 1411 ++++++++++++ src/librustc_codegen_llvm/build.rs | 16 + src/librustc_codegen_llvm/builder.rs | 1425 ++++++++++++ src/librustc_codegen_llvm/callee.rs | 232 ++ src/librustc_codegen_llvm/common.rs | 448 ++++ src/librustc_codegen_llvm/consts.rs | 322 +++ src/librustc_codegen_llvm/context.rs | 666 ++++++ .../debuginfo/create_scope_map.rs | 136 ++ src/librustc_codegen_llvm/debuginfo/doc.rs | 189 ++ src/librustc_codegen_llvm/debuginfo/gdb.rs | 88 + src/librustc_codegen_llvm/debuginfo/metadata.rs | 1773 +++++++++++++++ src/librustc_codegen_llvm/debuginfo/mod.rs | 542 +++++ src/librustc_codegen_llvm/debuginfo/namespace.rs | 66 + src/librustc_codegen_llvm/debuginfo/source_loc.rs | 107 + src/librustc_codegen_llvm/debuginfo/type_names.rs | 224 ++ src/librustc_codegen_llvm/debuginfo/utils.rs | 65 + src/librustc_codegen_llvm/declare.rs | 223 ++ src/librustc_codegen_llvm/diagnostics.rs | 55 + src/librustc_codegen_llvm/glue.rs | 122 + src/librustc_codegen_llvm/intrinsic.rs | 1465 ++++++++++++ src/librustc_codegen_llvm/lib.rs | 392 ++++ src/librustc_codegen_llvm/llvm_util.rs | 255 +++ src/librustc_codegen_llvm/metadata.rs | 124 + src/librustc_codegen_llvm/meth.rs | 115 + src/librustc_codegen_llvm/mir/analyze.rs | 361 +++ src/librustc_codegen_llvm/mir/block.rs | 895 ++++++++ src/librustc_codegen_llvm/mir/constant.rs | 271 +++ src/librustc_codegen_llvm/mir/mod.rs | 652 ++++++ src/librustc_codegen_llvm/mir/operand.rs | 427 ++++ src/librustc_codegen_llvm/mir/place.rs | 498 ++++ src/librustc_codegen_llvm/mir/rvalue.rs | 952 ++++++++ src/librustc_codegen_llvm/mir/statement.rs | 91 + src/librustc_codegen_llvm/mono_item.rs | 193 ++ src/librustc_codegen_llvm/time_graph.rs | 278 +++ src/librustc_codegen_llvm/type_.rs | 300 +++ src/librustc_codegen_llvm/type_of.rs | 506 +++++ src/librustc_codegen_llvm/value.rs | 24 + src/librustc_codegen_utils/Cargo.toml | 23 + src/librustc_codegen_utils/codegen_backend.rs | 296 +++ src/librustc_codegen_utils/lib.rs | 63 + src/librustc_codegen_utils/link.rs | 191 ++ src/librustc_codegen_utils/symbol_names.rs | 435 ++++ src/librustc_codegen_utils/symbol_names_test.rs | 79 + src/librustc_driver/Cargo.toml | 2 +- src/librustc_driver/driver.rs | 42 +- src/librustc_driver/lib.rs | 74 +- src/librustc_driver/pretty.rs | 8 +- src/librustc_driver/test.rs | 2 +- src/librustc_incremental/assert_dep_graph.rs | 6 +- src/librustc_incremental/assert_module_sources.rs | 18 +- src/librustc_incremental/persist/dirty_clean.rs | 4 +- src/librustc_lint/types.rs | 2 +- src/librustc_llvm/ffi.rs | 2 +- src/librustc_llvm/lib.rs | 2 +- src/librustc_metadata/diagnostics.rs | 6 +- src/librustc_metadata/encoder.rs | 9 +- src/librustc_mir/build/expr/as_rvalue.rs | 2 +- src/librustc_mir/build/expr/mod.rs | 4 +- src/librustc_mir/build/matches/mod.rs | 4 +- src/librustc_mir/build/mod.rs | 2 +- src/librustc_mir/build/scope.rs | 6 +- src/librustc_mir/hair/cx/block.rs | 2 +- src/librustc_mir/hair/cx/mod.rs | 2 +- src/librustc_mir/hair/mod.rs | 8 +- src/librustc_mir/monomorphize/collector.rs | 14 +- src/librustc_mir/monomorphize/item.rs | 18 +- src/librustc_mir/monomorphize/mod.rs | 20 +- src/librustc_mir/monomorphize/partitioning.rs | 120 +- src/librustc_mir/shim.rs | 4 +- src/librustc_mir/transform/add_call_guards.rs | 2 +- src/librustc_mir/transform/elaborate_drops.rs | 2 +- src/librustc_mir/transform/erase_regions.rs | 2 +- src/librustc_mir/transform/inline.rs | 16 +- src/librustc_mir/transform/mod.rs | 2 +- src/librustc_mir/transform/simplify.rs | 2 +- src/librustc_trans/Cargo.toml | 49 - src/librustc_trans/README.md | 7 - src/librustc_trans/abi.rs | 696 ------ src/librustc_trans/allocator.rs | 103 - src/librustc_trans/asm.rs | 129 -- src/librustc_trans/attributes.rs | 259 --- src/librustc_trans/back/archive.rs | 325 --- src/librustc_trans/back/bytecode.rs | 160 -- src/librustc_trans/back/command.rs | 175 -- src/librustc_trans/back/link.rs | 1626 ------------- src/librustc_trans/back/linker.rs | 1037 --------- src/librustc_trans/back/lto.rs | 773 ------- src/librustc_trans/back/rpath.rs | 282 --- src/librustc_trans/back/symbol_export.rs | 395 ---- src/librustc_trans/back/wasm.rs | 261 --- src/librustc_trans/back/write.rs | 2392 -------------------- src/librustc_trans/base.rs | 1411 ------------ src/librustc_trans/build.rs | 16 - src/librustc_trans/builder.rs | 1425 ------------ src/librustc_trans/callee.rs | 232 -- src/librustc_trans/common.rs | 449 ---- src/librustc_trans/consts.rs | 322 --- src/librustc_trans/context.rs | 666 ------ src/librustc_trans/debuginfo/create_scope_map.rs | 136 -- src/librustc_trans/debuginfo/doc.rs | 189 -- src/librustc_trans/debuginfo/gdb.rs | 88 - src/librustc_trans/debuginfo/metadata.rs | 1773 --------------- src/librustc_trans/debuginfo/mod.rs | 542 ----- src/librustc_trans/debuginfo/namespace.rs | 66 - src/librustc_trans/debuginfo/source_loc.rs | 107 - src/librustc_trans/debuginfo/type_names.rs | 224 -- src/librustc_trans/debuginfo/utils.rs | 65 - src/librustc_trans/declare.rs | 223 -- src/librustc_trans/diagnostics.rs | 55 - src/librustc_trans/glue.rs | 122 - src/librustc_trans/intrinsic.rs | 1465 ------------ src/librustc_trans/lib.rs | 390 ---- src/librustc_trans/llvm_util.rs | 255 --- src/librustc_trans/metadata.rs | 124 - src/librustc_trans/meth.rs | 115 - src/librustc_trans/mir/analyze.rs | 361 --- src/librustc_trans/mir/block.rs | 895 -------- src/librustc_trans/mir/constant.rs | 271 --- src/librustc_trans/mir/mod.rs | 652 ------ src/librustc_trans/mir/operand.rs | 427 ---- src/librustc_trans/mir/place.rs | 499 ---- src/librustc_trans/mir/rvalue.rs | 952 -------- src/librustc_trans/mir/statement.rs | 91 - src/librustc_trans/time_graph.rs | 278 --- src/librustc_trans/trans_item.rs | 193 -- src/librustc_trans/type_.rs | 300 --- src/librustc_trans/type_of.rs | 506 ----- src/librustc_trans/value.rs | 24 - src/librustc_trans_utils/Cargo.toml | 23 - src/librustc_trans_utils/lib.rs | 63 - src/librustc_trans_utils/link.rs | 191 -- src/librustc_trans_utils/symbol_names.rs | 435 ---- src/librustc_trans_utils/symbol_names_test.rs | 79 - src/librustc_trans_utils/trans_crate.rs | 296 --- src/librustc_typeck/check/intrinsic.rs | 2 +- src/librustc_typeck/check/op.rs | 2 +- src/librustc_typeck/coherence/builtin.rs | 2 +- src/librustc_typeck/collect.rs | 38 +- src/librustc_typeck/diagnostics.rs | 4 +- src/librustdoc/core.rs | 10 +- src/librustdoc/lib.rs | 2 +- src/librustdoc/test.rs | 16 +- src/libsyntax/feature_gate.rs | 4 +- src/libsyntax_ext/format.rs | 28 +- src/libsyntax_ext/format_foreign.rs | 4 +- src/libsyntax_ext/global_asm.rs | 2 +- src/rustllvm/PassWrapper.cpp | 2 +- .../item-collection/cross-crate-closures.rs | 18 +- .../cross-crate-generic-functions.rs | 12 +- .../item-collection/cross-crate-trait-method.rs | 24 +- .../item-collection/drop_in_place_intrinsic.rs | 14 +- .../item-collection/function-as-argument.rs | 24 +- .../item-collection/generic-drop-glue.rs | 26 +- .../item-collection/generic-functions.rs | 30 +- .../codegen-units/item-collection/generic-impl.rs | 38 +- .../impl-in-non-instantiated-generic.rs | 6 +- .../instantiation-through-vtable.rs | 16 +- .../item-collection/items-within-generic-items.rs | 14 +- .../item-collection/non-generic-closures.rs | 26 +- .../item-collection/non-generic-drop-glue.rs | 12 +- .../item-collection/non-generic-functions.rs | 26 +- .../item-collection/overloaded-operators.rs | 14 +- .../codegen-units/item-collection/static-init.rs | 9 +- .../item-collection/statics-and-consts.rs | 14 +- .../item-collection/trait-implementations.rs | 24 +- .../item-collection/trait-method-as-argument.rs | 32 +- .../item-collection/trait-method-default-impl.rs | 20 +- .../item-collection/transitive-drop-glue.rs | 28 +- .../item-collection/tuple-drop-glue.rs | 14 +- .../item-collection/unreferenced-const-fn.rs | 4 +- .../unreferenced-inline-function.rs | 5 +- src/test/codegen-units/item-collection/unsizing.rs | 20 +- .../item-collection/unused-traits-and-generics.rs | 4 +- .../codegen-units/partitioning/extern-drop-glue.rs | 12 +- .../codegen-units/partitioning/extern-generic.rs | 16 +- .../partitioning/inlining-from-extern-crate.rs | 18 +- .../codegen-units/partitioning/local-drop-glue.rs | 16 +- .../codegen-units/partitioning/local-generic.rs | 18 +- .../partitioning/local-inlining-but-not-all.rs | 10 +- .../codegen-units/partitioning/local-inlining.rs | 10 +- .../partitioning/local-transitive-inlining.rs | 10 +- .../partitioning/methods-are-with-self-type.rs | 22 +- .../codegen-units/partitioning/regular-modules.rs | 44 +- .../codegen-units/partitioning/shared-generics.rs | 8 +- src/test/codegen-units/partitioning/statics.rs | 22 +- .../partitioning/vtable-through-const.rs | 20 +- src/test/compile-fail/const-err.rs | 2 +- src/test/compile-fail/const-eval-overflow2.rs | 2 +- src/test/compile-fail/const-eval-overflow2b.rs | 2 +- src/test/compile-fail/const-eval-overflow2c.rs | 2 +- .../compile-fail/dep-graph-assoc-type-codegen.rs | 47 + .../compile-fail/dep-graph-assoc-type-trans.rs | 47 - src/test/debuginfo/struct-with-destructor.rs | 2 +- .../debuginfo/var-captured-in-sendable-closure.rs | 2 +- src/test/incremental/cache_file_headers.rs | 4 +- .../incremental/change_add_field/struct_point.rs | 16 +- .../incremental/change_private_fn/struct_point.rs | 2 +- .../change_private_impl_method/struct_point.rs | 2 +- .../struct_point.rs | 2 +- .../change_pub_inherent_method_sig/struct_point.rs | 6 +- .../incremental/change_symbol_export_status.rs | 2 +- src/test/incremental/commandline-args.rs | 2 +- src/test/incremental/inlined_hir_34991/main.rs | 2 +- src/test/incremental/issue-38222.rs | 2 +- src/test/incremental/issue-49595/issue_49595.rs | 4 +- src/test/incremental/remapped_paths_cc/main.rs | 2 +- src/test/incremental/spike-neg1.rs | 2 +- src/test/incremental/spike-neg2.rs | 8 +- src/test/incremental/spike.rs | 2 +- .../run-fail/mir_codegen_calls_converging_drops.rs | 34 + .../mir_codegen_calls_converging_drops_2.rs | 38 + src/test/run-fail/mir_codegen_calls_diverging.rs | 23 + .../run-fail/mir_codegen_calls_diverging_drops.rs | 32 + src/test/run-fail/mir_codegen_no_landing_pads.rs | 37 + .../mir_codegen_no_landing_pads_diverging.rs | 37 + .../run-fail/mir_trans_calls_converging_drops.rs | 34 - .../run-fail/mir_trans_calls_converging_drops_2.rs | 38 - src/test/run-fail/mir_trans_calls_diverging.rs | 23 - .../run-fail/mir_trans_calls_diverging_drops.rs | 32 - src/test/run-fail/mir_trans_no_landing_pads.rs | 37 - .../mir_trans_no_landing_pads_diverging.rs | 37 - src/test/run-fail/rhs-type.rs | 2 +- .../hotplug_codegen_backend/the_backend.rs | 26 +- src/test/run-make-fulldeps/issue-19371/foo.rs | 16 +- src/test/run-make-fulldeps/issue-7349/Makefile | 2 +- src/test/run-pass-fulldeps/compiler-calls.rs | 6 +- .../associated-types-region-erasure-issue-20582.rs | 2 +- src/test/run-pass/atomic-compare_exchange.rs | 2 +- src/test/run-pass/codegen-object-shim.rs | 14 + src/test/run-pass/codegen-tag-static-padding.rs | 67 + src/test/run-pass/compiletest-skip-codegen.rs | 17 + src/test/run-pass/compiletest-skip-trans.rs | 17 - src/test/run-pass/conditional-compile.rs | 2 +- src/test/run-pass/issue-18425.rs | 2 +- src/test/run-pass/issue-18514.rs | 2 +- src/test/run-pass/issue-18652.rs | 2 +- src/test/run-pass/issue-18661.rs | 2 +- src/test/run-pass/issue-20644.rs | 2 +- src/test/run-pass/issue-24085.rs | 2 +- src/test/run-pass/issue-34569.rs | 2 +- src/test/run-pass/issue-36381.rs | 2 +- src/test/run-pass/issue-38002.rs | 2 +- src/test/run-pass/issue-5243.rs | 2 +- .../run-pass/issue24687-embed-debuginfo/main.rs | 2 +- .../method-two-trait-defer-resolution-2.rs | 2 +- src/test/run-pass/mir_codegen_array.rs | 19 + src/test/run-pass/mir_codegen_array_2.rs | 18 + src/test/run-pass/mir_codegen_call_converging.rs | 26 + src/test/run-pass/mir_codegen_calls.rs | 200 ++ src/test/run-pass/mir_codegen_calls_variadic.rs | 31 + src/test/run-pass/mir_codegen_critical_edge.rs | 52 + src/test/run-pass/mir_codegen_spike1.rs | 21 + src/test/run-pass/mir_codegen_switch.rs | 44 + src/test/run-pass/mir_codegen_switchint.rs | 21 + src/test/run-pass/mir_overflow_off.rs | 2 +- src/test/run-pass/mir_trans_array.rs | 19 - src/test/run-pass/mir_trans_array_2.rs | 18 - src/test/run-pass/mir_trans_call_converging.rs | 26 - src/test/run-pass/mir_trans_calls.rs | 200 -- src/test/run-pass/mir_trans_calls_variadic.rs | 31 - src/test/run-pass/mir_trans_critical_edge.rs | 52 - src/test/run-pass/mir_trans_spike1.rs | 21 - src/test/run-pass/mir_trans_switch.rs | 44 - src/test/run-pass/mir_trans_switchint.rs | 21 - src/test/run-pass/pattern-bound-var-in-for-each.rs | 4 +- src/test/run-pass/regions-mock-codegen.rs | 62 + src/test/run-pass/regions-mock-trans.rs | 62 - src/test/run-pass/sepcomp-fns-backwards.rs | 2 +- src/test/run-pass/small-enum-range-edge.rs | 2 +- src/test/run-pass/trans-object-shim.rs | 14 - src/test/run-pass/trans-tag-static-padding.rs | 67 - src/test/run-pass/union/union-const-codegen.rs | 25 + src/test/run-pass/union/union-const-trans.rs | 25 - src/test/run-pass/zero-sized-tuple-struct.rs | 2 +- src/tools/compiletest/src/header.rs | 12 +- src/tools/compiletest/src/runtest.rs | 24 +- src/tools/tidy/src/deps.rs | 2 +- 344 files changed, 27633 insertions(+), 27634 deletions(-) create mode 100644 src/librustc/traits/codegen/mod.rs delete mode 100644 src/librustc/traits/trans/mod.rs create mode 100644 src/librustc_codegen_llvm/Cargo.toml create mode 100644 src/librustc_codegen_llvm/README.md create mode 100644 src/librustc_codegen_llvm/abi.rs create mode 100644 src/librustc_codegen_llvm/allocator.rs create mode 100644 src/librustc_codegen_llvm/asm.rs create mode 100644 src/librustc_codegen_llvm/attributes.rs create mode 100644 src/librustc_codegen_llvm/back/archive.rs create mode 100644 src/librustc_codegen_llvm/back/bytecode.rs create mode 100644 src/librustc_codegen_llvm/back/command.rs create mode 100644 src/librustc_codegen_llvm/back/link.rs create mode 100644 src/librustc_codegen_llvm/back/linker.rs create mode 100644 src/librustc_codegen_llvm/back/lto.rs create mode 100644 src/librustc_codegen_llvm/back/rpath.rs create mode 100644 src/librustc_codegen_llvm/back/symbol_export.rs create mode 100644 src/librustc_codegen_llvm/back/wasm.rs create mode 100644 src/librustc_codegen_llvm/back/write.rs create mode 100644 src/librustc_codegen_llvm/base.rs create mode 100644 src/librustc_codegen_llvm/build.rs create mode 100644 src/librustc_codegen_llvm/builder.rs create mode 100644 src/librustc_codegen_llvm/callee.rs create mode 100644 src/librustc_codegen_llvm/common.rs create mode 100644 src/librustc_codegen_llvm/consts.rs create mode 100644 src/librustc_codegen_llvm/context.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/create_scope_map.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/doc.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/gdb.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/metadata.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/mod.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/namespace.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/source_loc.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/type_names.rs create mode 100644 src/librustc_codegen_llvm/debuginfo/utils.rs create mode 100644 src/librustc_codegen_llvm/declare.rs create mode 100644 src/librustc_codegen_llvm/diagnostics.rs create mode 100644 src/librustc_codegen_llvm/glue.rs create mode 100644 src/librustc_codegen_llvm/intrinsic.rs create mode 100644 src/librustc_codegen_llvm/lib.rs create mode 100644 src/librustc_codegen_llvm/llvm_util.rs create mode 100644 src/librustc_codegen_llvm/metadata.rs create mode 100644 src/librustc_codegen_llvm/meth.rs create mode 100644 src/librustc_codegen_llvm/mir/analyze.rs create mode 100644 src/librustc_codegen_llvm/mir/block.rs create mode 100644 src/librustc_codegen_llvm/mir/constant.rs create mode 100644 src/librustc_codegen_llvm/mir/mod.rs create mode 100644 src/librustc_codegen_llvm/mir/operand.rs create mode 100644 src/librustc_codegen_llvm/mir/place.rs create mode 100644 src/librustc_codegen_llvm/mir/rvalue.rs create mode 100644 src/librustc_codegen_llvm/mir/statement.rs create mode 100644 src/librustc_codegen_llvm/mono_item.rs create mode 100644 src/librustc_codegen_llvm/time_graph.rs create mode 100644 src/librustc_codegen_llvm/type_.rs create mode 100644 src/librustc_codegen_llvm/type_of.rs create mode 100644 src/librustc_codegen_llvm/value.rs create mode 100644 src/librustc_codegen_utils/Cargo.toml create mode 100644 src/librustc_codegen_utils/codegen_backend.rs create mode 100644 src/librustc_codegen_utils/lib.rs create mode 100644 src/librustc_codegen_utils/link.rs create mode 100644 src/librustc_codegen_utils/symbol_names.rs create mode 100644 src/librustc_codegen_utils/symbol_names_test.rs delete mode 100644 src/librustc_trans/Cargo.toml delete mode 100644 src/librustc_trans/README.md delete mode 100644 src/librustc_trans/abi.rs delete mode 100644 src/librustc_trans/allocator.rs delete mode 100644 src/librustc_trans/asm.rs delete mode 100644 src/librustc_trans/attributes.rs delete mode 100644 src/librustc_trans/back/archive.rs delete mode 100644 src/librustc_trans/back/bytecode.rs delete mode 100644 src/librustc_trans/back/command.rs delete mode 100644 src/librustc_trans/back/link.rs delete mode 100644 src/librustc_trans/back/linker.rs delete mode 100644 src/librustc_trans/back/lto.rs delete mode 100644 src/librustc_trans/back/rpath.rs delete mode 100644 src/librustc_trans/back/symbol_export.rs delete mode 100644 src/librustc_trans/back/wasm.rs delete mode 100644 src/librustc_trans/back/write.rs delete mode 100644 src/librustc_trans/base.rs delete mode 100644 src/librustc_trans/build.rs delete mode 100644 src/librustc_trans/builder.rs delete mode 100644 src/librustc_trans/callee.rs delete mode 100644 src/librustc_trans/common.rs delete mode 100644 src/librustc_trans/consts.rs delete mode 100644 src/librustc_trans/context.rs delete mode 100644 src/librustc_trans/debuginfo/create_scope_map.rs delete mode 100644 src/librustc_trans/debuginfo/doc.rs delete mode 100644 src/librustc_trans/debuginfo/gdb.rs delete mode 100644 src/librustc_trans/debuginfo/metadata.rs delete mode 100644 src/librustc_trans/debuginfo/mod.rs delete mode 100644 src/librustc_trans/debuginfo/namespace.rs delete mode 100644 src/librustc_trans/debuginfo/source_loc.rs delete mode 100644 src/librustc_trans/debuginfo/type_names.rs delete mode 100644 src/librustc_trans/debuginfo/utils.rs delete mode 100644 src/librustc_trans/declare.rs delete mode 100644 src/librustc_trans/diagnostics.rs delete mode 100644 src/librustc_trans/glue.rs delete mode 100644 src/librustc_trans/intrinsic.rs delete mode 100644 src/librustc_trans/lib.rs delete mode 100644 src/librustc_trans/llvm_util.rs delete mode 100644 src/librustc_trans/metadata.rs delete mode 100644 src/librustc_trans/meth.rs delete mode 100644 src/librustc_trans/mir/analyze.rs delete mode 100644 src/librustc_trans/mir/block.rs delete mode 100644 src/librustc_trans/mir/constant.rs delete mode 100644 src/librustc_trans/mir/mod.rs delete mode 100644 src/librustc_trans/mir/operand.rs delete mode 100644 src/librustc_trans/mir/place.rs delete mode 100644 src/librustc_trans/mir/rvalue.rs delete mode 100644 src/librustc_trans/mir/statement.rs delete mode 100644 src/librustc_trans/time_graph.rs delete mode 100644 src/librustc_trans/trans_item.rs delete mode 100644 src/librustc_trans/type_.rs delete mode 100644 src/librustc_trans/type_of.rs delete mode 100644 src/librustc_trans/value.rs delete mode 100644 src/librustc_trans_utils/Cargo.toml delete mode 100644 src/librustc_trans_utils/lib.rs delete mode 100644 src/librustc_trans_utils/link.rs delete mode 100644 src/librustc_trans_utils/symbol_names.rs delete mode 100644 src/librustc_trans_utils/symbol_names_test.rs delete mode 100644 src/librustc_trans_utils/trans_crate.rs create mode 100644 src/test/compile-fail/dep-graph-assoc-type-codegen.rs delete mode 100644 src/test/compile-fail/dep-graph-assoc-type-trans.rs create mode 100644 src/test/run-fail/mir_codegen_calls_converging_drops.rs create mode 100644 src/test/run-fail/mir_codegen_calls_converging_drops_2.rs create mode 100644 src/test/run-fail/mir_codegen_calls_diverging.rs create mode 100644 src/test/run-fail/mir_codegen_calls_diverging_drops.rs create mode 100644 src/test/run-fail/mir_codegen_no_landing_pads.rs create mode 100644 src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs delete mode 100644 src/test/run-fail/mir_trans_calls_converging_drops.rs delete mode 100644 src/test/run-fail/mir_trans_calls_converging_drops_2.rs delete mode 100644 src/test/run-fail/mir_trans_calls_diverging.rs delete mode 100644 src/test/run-fail/mir_trans_calls_diverging_drops.rs delete mode 100644 src/test/run-fail/mir_trans_no_landing_pads.rs delete mode 100644 src/test/run-fail/mir_trans_no_landing_pads_diverging.rs create mode 100644 src/test/run-pass/codegen-object-shim.rs create mode 100644 src/test/run-pass/codegen-tag-static-padding.rs create mode 100644 src/test/run-pass/compiletest-skip-codegen.rs delete mode 100644 src/test/run-pass/compiletest-skip-trans.rs create mode 100644 src/test/run-pass/mir_codegen_array.rs create mode 100644 src/test/run-pass/mir_codegen_array_2.rs create mode 100644 src/test/run-pass/mir_codegen_call_converging.rs create mode 100644 src/test/run-pass/mir_codegen_calls.rs create mode 100644 src/test/run-pass/mir_codegen_calls_variadic.rs create mode 100644 src/test/run-pass/mir_codegen_critical_edge.rs create mode 100644 src/test/run-pass/mir_codegen_spike1.rs create mode 100644 src/test/run-pass/mir_codegen_switch.rs create mode 100644 src/test/run-pass/mir_codegen_switchint.rs delete mode 100644 src/test/run-pass/mir_trans_array.rs delete mode 100644 src/test/run-pass/mir_trans_array_2.rs delete mode 100644 src/test/run-pass/mir_trans_call_converging.rs delete mode 100644 src/test/run-pass/mir_trans_calls.rs delete mode 100644 src/test/run-pass/mir_trans_calls_variadic.rs delete mode 100644 src/test/run-pass/mir_trans_critical_edge.rs delete mode 100644 src/test/run-pass/mir_trans_spike1.rs delete mode 100644 src/test/run-pass/mir_trans_switch.rs delete mode 100644 src/test/run-pass/mir_trans_switchint.rs create mode 100644 src/test/run-pass/regions-mock-codegen.rs delete mode 100644 src/test/run-pass/regions-mock-trans.rs delete mode 100644 src/test/run-pass/trans-object-shim.rs delete mode 100644 src/test/run-pass/trans-tag-static-padding.rs create mode 100644 src/test/run-pass/union/union-const-codegen.rs delete mode 100644 src/test/run-pass/union/union-const-trans.rs (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index 76ca287b815..00cc530e632 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -1896,6 +1896,52 @@ dependencies = [ "syntax_pos 0.0.0", ] +[[package]] +name = "rustc_codegen_llvm" +version = "0.0.0" +dependencies = [ + "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", + "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "jobserver 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", + "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc_allocator 0.0.0", + "rustc_apfloat 0.0.0", + "rustc_codegen_utils 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_errors 0.0.0", + "rustc_incremental 0.0.0", + "rustc_llvm 0.0.0", + "rustc_mir 0.0.0", + "rustc_platform_intrinsics 0.0.0", + "rustc_target 0.0.0", + "serialize 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", + "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", +] + +[[package]] +name = "rustc_codegen_utils" +version = "0.0.0" +dependencies = [ + "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", + "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", + "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", + "rustc 0.0.0", + "rustc_data_structures 0.0.0", + "rustc_incremental 0.0.0", + "rustc_mir 0.0.0", + "rustc_target 0.0.0", + "syntax 0.0.0", + "syntax_pos 0.0.0", +] + [[package]] name = "rustc_cratesio_shim" version = "0.0.0" @@ -1932,6 +1978,7 @@ dependencies = [ "rustc-rayon 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc_allocator 0.0.0", "rustc_borrowck 0.0.0", + "rustc_codegen_utils 0.0.0", "rustc_data_structures 0.0.0", "rustc_errors 0.0.0", "rustc_incremental 0.0.0", @@ -1945,7 +1992,6 @@ dependencies = [ "rustc_save_analysis 0.0.0", "rustc_target 0.0.0", "rustc_traits 0.0.0", - "rustc_trans_utils 0.0.0", "rustc_typeck 0.0.0", "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", @@ -2155,52 +2201,6 @@ dependencies = [ "syntax_pos 0.0.0", ] -[[package]] -name = "rustc_trans" -version = "0.0.0" -dependencies = [ - "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "cc 1.0.15 (registry+https://github.com/rust-lang/crates.io-index)", - "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "jobserver 0.1.11 (registry+https://github.com/rust-lang/crates.io-index)", - "libc 0.2.40 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "num_cpus 1.8.0 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc-demangle 0.1.7 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc_allocator 0.0.0", - "rustc_apfloat 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_errors 0.0.0", - "rustc_incremental 0.0.0", - "rustc_llvm 0.0.0", - "rustc_mir 0.0.0", - "rustc_platform_intrinsics 0.0.0", - "rustc_target 0.0.0", - "rustc_trans_utils 0.0.0", - "serialize 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", - "tempdir 0.3.7 (registry+https://github.com/rust-lang/crates.io-index)", -] - -[[package]] -name = "rustc_trans_utils" -version = "0.0.0" -dependencies = [ - "ar 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", - "flate2 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", - "log 0.4.1 (registry+https://github.com/rust-lang/crates.io-index)", - "rustc 0.0.0", - "rustc_data_structures 0.0.0", - "rustc_incremental 0.0.0", - "rustc_mir 0.0.0", - "rustc_target 0.0.0", - "syntax 0.0.0", - "syntax_pos 0.0.0", -] - [[package]] name = "rustc_tsan" version = "0.0.0" diff --git a/src/Cargo.toml b/src/Cargo.toml index 35858ee2868..1518e8d910f 100644 --- a/src/Cargo.toml +++ b/src/Cargo.toml @@ -4,7 +4,7 @@ members = [ "rustc", "libstd", "libtest", - "librustc_trans", + "librustc_codegen_llvm", "tools/cargotest", "tools/clippy", "tools/compiletest", diff --git a/src/bootstrap/builder.rs b/src/bootstrap/builder.rs index e5824010ef2..cc1e66332a3 100644 --- a/src/bootstrap/builder.rs +++ b/src/bootstrap/builder.rs @@ -844,7 +844,7 @@ impl<'a> Builder<'a> { // default via `-ldylib=winapi_foo`. That is, they're linked with the // `dylib` type with a `winapi_` prefix (so the winapi ones don't // conflict with the system MinGW ones). This consequently means that - // the binaries we ship of things like rustc_trans (aka the rustc_trans + // the binaries we ship of things like rustc_codegen_llvm (aka the rustc_codegen_llvm // DLL) when linked against *again*, for example with procedural macros // or plugins, will trigger the propagation logic of `-ldylib`, passing // `-lwinapi_foo` to the linker again. This isn't actually available in diff --git a/src/bootstrap/check.rs b/src/bootstrap/check.rs index 64354ae29aa..a516af58b1e 100644 --- a/src/bootstrap/check.rs +++ b/src/bootstrap/check.rs @@ -118,7 +118,7 @@ impl Step for CodegenBackend { const DEFAULT: bool = true; fn should_run(run: ShouldRun) -> ShouldRun { - run.all_krates("rustc_trans") + run.all_krates("rustc_codegen_llvm") } fn make_run(run: RunConfig) { @@ -139,12 +139,12 @@ impl Step for CodegenBackend { let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "check"); let features = builder.rustc_features().to_string(); - cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_trans/Cargo.toml")); + cargo.arg("--manifest-path").arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml")); rustc_cargo_env(builder, &mut cargo); // We won't build LLVM if it's not available, as it shouldn't affect `check`. - let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage)); + let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage)); run_cargo(builder, cargo.arg("--features").arg(features), &codegen_backend_stamp(builder, compiler, target, backend), @@ -259,14 +259,14 @@ pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned, backend: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Librustc, target) - .join(format!(".librustc_trans-{}-check.stamp", backend)) + .join(format!(".librustc_codegen_llvm-{}-check.stamp", backend)) } /// Cargo's output path for rustdoc in a given stage, compiled by a particular diff --git a/src/bootstrap/compile.rs b/src/bootstrap/compile.rs index 1248c2b50be..8e0227f8fe1 100644 --- a/src/bootstrap/compile.rs +++ b/src/bootstrap/compile.rs @@ -603,7 +603,7 @@ impl Step for CodegenBackend { const DEFAULT: bool = true; fn should_run(run: ShouldRun) -> ShouldRun { - run.all_krates("rustc_trans") + run.all_krates("rustc_codegen_llvm") } fn make_run(run: RunConfig) { @@ -637,7 +637,7 @@ impl Step for CodegenBackend { let mut cargo = builder.cargo(compiler, Mode::Librustc, target, "build"); let mut features = builder.rustc_features().to_string(); cargo.arg("--manifest-path") - .arg(builder.src.join("src/librustc_trans/Cargo.toml")); + .arg(builder.src.join("src/librustc_codegen_llvm/Cargo.toml")); rustc_cargo_env(builder, &mut cargo); features += &build_codegen_backend(&builder, &mut cargo, &compiler, target, backend); @@ -645,7 +645,7 @@ impl Step for CodegenBackend { let tmp_stamp = builder.cargo_out(compiler, Mode::Librustc, target) .join(".tmp.stamp"); - let _folder = builder.fold_output(|| format!("stage{}-rustc_trans", compiler.stage)); + let _folder = builder.fold_output(|| format!("stage{}-rustc_codegen_llvm", compiler.stage)); let files = run_cargo(builder, cargo.arg("--features").arg(features), &tmp_stamp, @@ -656,7 +656,7 @@ impl Step for CodegenBackend { let mut files = files.into_iter() .filter(|f| { let filename = f.file_name().unwrap().to_str().unwrap(); - is_dylib(filename) && filename.contains("rustc_trans-") + is_dylib(filename) && filename.contains("rustc_codegen_llvm-") }); let codegen_backend = match files.next() { Some(f) => f, @@ -697,7 +697,7 @@ pub fn build_codegen_backend(builder: &Builder, compiler.stage, &compiler.host, target, backend)); // Pass down configuration from the LLVM build into the build of - // librustc_llvm and librustc_trans. + // librustc_llvm and librustc_codegen_llvm. if builder.is_rust_llvm(target) { cargo.env("LLVM_RUSTLLVM", "1"); } @@ -762,7 +762,7 @@ fn copy_codegen_backends_to_sysroot(builder: &Builder, t!(t!(File::open(&stamp)).read_to_string(&mut dylib)); let file = Path::new(&dylib); let filename = file.file_name().unwrap().to_str().unwrap(); - // change `librustc_trans-xxxxxx.so` to `librustc_trans-llvm.so` + // change `librustc_codegen_llvm-xxxxxx.so` to `librustc_codegen_llvm-llvm.so` let target_filename = { let dash = filename.find("-").unwrap(); let dot = filename.find(".").unwrap(); @@ -808,14 +808,14 @@ pub fn librustc_stamp(builder: &Builder, compiler: Compiler, target: Interned, backend: Interned) -> PathBuf { builder.cargo_out(compiler, Mode::Librustc, target) - .join(format!(".librustc_trans-{}.stamp", backend)) + .join(format!(".librustc_codegen_llvm-{}.stamp", backend)) } pub fn compiler_file(builder: &Builder, diff --git a/src/doc/rustc-ux-guidelines.md b/src/doc/rustc-ux-guidelines.md index b62762ef69e..93e94e55863 100644 --- a/src/doc/rustc-ux-guidelines.md +++ b/src/doc/rustc-ux-guidelines.md @@ -69,7 +69,7 @@ for details on how to format and write long error codes. [librustc_passes](https://github.com/rust-lang/rust/blob/master/src/librustc_passes/diagnostics.rs), [librustc_privacy](https://github.com/rust-lang/rust/blob/master/src/librustc_privacy/diagnostics.rs), [librustc_resolve](https://github.com/rust-lang/rust/blob/master/src/librustc_resolve/diagnostics.rs), - [librustc_trans](https://github.com/rust-lang/rust/blob/master/src/librustc_trans/diagnostics.rs), + [librustc_codegen_llvm](https://github.com/rust-lang/rust/blob/master/src/librustc_codegen_llvm/diagnostics.rs), [librustc_plugin](https://github.com/rust-lang/rust/blob/master/src/librustc_plugin/diagnostics.rs), [librustc_typeck](https://github.com/rust-lang/rust/blob/master/src/librustc_typeck/diagnostics.rs). * Explanations have full markdown support. Use it, especially to highlight diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index f59c9f7fd61..09d16b26520 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -134,7 +134,7 @@ unsafe impl Alloc for Global { } /// The allocator for unique pointers. -// This function must not unwind. If it does, MIR trans will fail. +// This function must not unwind. If it does, MIR codegen will fail. #[cfg(not(test))] #[lang = "exchange_malloc"] #[inline] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 7d3e7af1a18..5ec6cb6c710 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -10,7 +10,7 @@ //! rustc compiler intrinsics. //! -//! The corresponding definitions are in librustc_trans/intrinsic.rs. +//! The corresponding definitions are in librustc_codegen_llvm/intrinsic.rs. //! //! # Volatiles //! diff --git a/src/librustc/Cargo.toml b/src/librustc/Cargo.toml index af108188ce0..8f9a8bd5c01 100644 --- a/src/librustc/Cargo.toml +++ b/src/librustc/Cargo.toml @@ -35,13 +35,13 @@ byteorder = { version = "1.1", features = ["i128"]} # rlib/dylib pair but all crates.io crates tend to just be rlibs. This means # we've got a problem for dependency graphs that look like: # -# foo - rustc_trans +# foo - rustc_codegen_llvm # / \ # rustc ---- rustc_driver # \ / # foo - rustc_metadata # -# Here the crate `foo` is linked into the `rustc_trans` and the +# Here the crate `foo` is linked into the `rustc_codegen_llvm` and the # `rustc_metadata` dylibs, meaning we've got duplicate copies! When we then # go to link `rustc_driver` the compiler notices this and gives us a compiler # error. @@ -49,7 +49,7 @@ byteorder = { version = "1.1", features = ["i128"]} # To work around this problem we just add these crates.io dependencies to the # `rustc` crate which is a shared dependency above. That way the crate `foo` # shows up in the dylib for the `rustc` crate, deduplicating it and allowing -# crates like `rustc_trans` to use `foo` *through* the `rustc` crate. +# crates like `rustc_codegen_llvm` to use `foo` *through* the `rustc` crate. # # tl;dr; this is not needed to get `rustc` to compile, but if you remove it then # later crate stop compiling. If you can remove this and everything diff --git a/src/librustc/cfg/construct.rs b/src/librustc/cfg/construct.rs index 7e03288f572..20e4b1343c1 100644 --- a/src/librustc/cfg/construct.rs +++ b/src/librustc/cfg/construct.rs @@ -452,13 +452,13 @@ impl<'a, 'tcx> CFGBuilder<'a, 'tcx> { // The CFG for match expression is quite complex, so no ASCII // art for it (yet). // - // The CFG generated below matches roughly what trans puts - // out. Each pattern and guard is visited in parallel, with + // The CFG generated below matches roughly what MIR contains. + // Each pattern and guard is visited in parallel, with // arms containing multiple patterns generating multiple nodes // for the same guard expression. The guard expressions chain // into each other from top to bottom, with a specific // exception to allow some additional valid programs - // (explained below). Trans differs slightly in that the + // (explained below). MIR differs slightly in that the // pattern matching may continue after a guard but the visible // behaviour should be the same. // diff --git a/src/librustc/dep_graph/dep_node.rs b/src/librustc/dep_graph/dep_node.rs index 4847a7f4ddb..9cd9d44874b 100644 --- a/src/librustc/dep_graph/dep_node.rs +++ b/src/librustc/dep_graph/dep_node.rs @@ -565,7 +565,7 @@ define_dep_nodes!( <'tcx> [] IsUnreachableLocalDefinition(DefId), [] IsMirAvailable(DefId), [] ItemAttrs(DefId), - [] TransFnAttrs(DefId), + [] CodegenFnAttrs(DefId), [] FnArgNames(DefId), [] RenderedConst(DefId), [] DylibDepFormats(CrateNum), @@ -637,8 +637,8 @@ define_dep_nodes!( <'tcx> [eval_always] AllTraits, [input] AllCrateNums, [] ExportedSymbols(CrateNum), - [eval_always] CollectAndPartitionTranslationItems, - [] IsTranslatedItem(DefId), + [eval_always] CollectAndPartitionMonoItems, + [] IsCodegenedItem(DefId), [] CodegenUnit(InternedString), [] CompileCodegenUnit(InternedString), [input] OutputFilenames, diff --git a/src/librustc/dep_graph/graph.rs b/src/librustc/dep_graph/graph.rs index 797332e699d..26470ddc82a 100644 --- a/src/librustc/dep_graph/graph.rs +++ b/src/librustc/dep_graph/graph.rs @@ -856,10 +856,10 @@ impl DepGraph { /// each partition. In the first run, we create partitions based on /// the symbols that need to be compiled. For each partition P, we /// hash the symbols in P and create a `WorkProduct` record associated -/// with `DepNode::TransPartition(P)`; the hash is the set of symbols +/// with `DepNode::CodegenUnit(P)`; the hash is the set of symbols /// in P. /// -/// The next time we compile, if the `DepNode::TransPartition(P)` is +/// The next time we compile, if the `DepNode::CodegenUnit(P)` is /// judged to be clean (which means none of the things we read to /// generate the partition were found to be dirty), it will be loaded /// into previous work products. We will then regenerate the set of diff --git a/src/librustc/hir/check_attr.rs b/src/librustc/hir/check_attr.rs index 24a1256c9d3..591cb9d5ad6 100644 --- a/src/librustc/hir/check_attr.rs +++ b/src/librustc/hir/check_attr.rs @@ -58,7 +58,7 @@ impl<'a, 'tcx> CheckAttrVisitor<'a, 'tcx> { /// Check any attribute. fn check_attributes(&self, item: &hir::Item, target: Target) { if target == Target::Fn { - self.tcx.trans_fn_attrs(self.tcx.hir.local_def_id(item.id)); + self.tcx.codegen_fn_attrs(self.tcx.hir.local_def_id(item.id)); } else if let Some(a) = item.attrs.iter().find(|a| a.check_name("target_feature")) { self.tcx.sess.struct_span_err(a.span, "attribute should be applied to a function") .span_label(item.span, "not a function") diff --git a/src/librustc/hir/mod.rs b/src/librustc/hir/mod.rs index c48fb7ab7eb..edd5e668d5e 100644 --- a/src/librustc/hir/mod.rs +++ b/src/librustc/hir/mod.rs @@ -2244,8 +2244,8 @@ pub fn provide(providers: &mut Providers) { } #[derive(Clone, RustcEncodable, RustcDecodable, Hash)] -pub struct TransFnAttrs { - pub flags: TransFnAttrFlags, +pub struct CodegenFnAttrs { + pub flags: CodegenFnAttrFlags, pub inline: InlineAttr, pub export_name: Option, pub target_features: Vec, @@ -2254,7 +2254,7 @@ pub struct TransFnAttrs { bitflags! { #[derive(RustcEncodable, RustcDecodable)] - pub struct TransFnAttrFlags: u8 { + pub struct CodegenFnAttrFlags: u8 { const COLD = 0b0000_0001; const ALLOCATOR = 0b0000_0010; const UNWIND = 0b0000_0100; @@ -2266,10 +2266,10 @@ bitflags! { } } -impl TransFnAttrs { - pub fn new() -> TransFnAttrs { - TransFnAttrs { - flags: TransFnAttrFlags::empty(), +impl CodegenFnAttrs { + pub fn new() -> CodegenFnAttrs { + CodegenFnAttrs { + flags: CodegenFnAttrFlags::empty(), inline: InlineAttr::None, export_name: None, target_features: vec![], @@ -2287,7 +2287,6 @@ impl TransFnAttrs { /// True if `#[no_mangle]` or `#[export_name(...)]` is present. pub fn contains_extern_indicator(&self) -> bool { - self.flags.contains(TransFnAttrFlags::NO_MANGLE) || self.export_name.is_some() + self.flags.contains(CodegenFnAttrFlags::NO_MANGLE) || self.export_name.is_some() } } - diff --git a/src/librustc/ich/impls_hir.rs b/src/librustc/ich/impls_hir.rs index ed01704e6a4..30f725a6b50 100644 --- a/src/librustc/ich/impls_hir.rs +++ b/src/librustc/ich/impls_hir.rs @@ -1155,12 +1155,12 @@ impl<'a> ToStableHashKey> for hir::TraitCandidate { } } -impl<'hir> HashStable> for hir::TransFnAttrs +impl<'hir> HashStable> for hir::CodegenFnAttrs { fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, hasher: &mut StableHasher) { - let hir::TransFnAttrs { + let hir::CodegenFnAttrs { flags, inline, export_name, @@ -1176,7 +1176,7 @@ impl<'hir> HashStable> for hir::TransFnAttrs } } -impl<'hir> HashStable> for hir::TransFnAttrFlags +impl<'hir> HashStable> for hir::CodegenFnAttrFlags { fn hash_stable(&self, hcx: &mut StableHashingContext<'hir>, diff --git a/src/librustc/ich/mod.rs b/src/librustc/ich/mod.rs index a0c6bbbb239..d58eb64c366 100644 --- a/src/librustc/ich/mod.rs +++ b/src/librustc/ich/mod.rs @@ -30,7 +30,7 @@ pub const ATTR_CLEAN: &'static str = "rustc_clean"; pub const ATTR_IF_THIS_CHANGED: &'static str = "rustc_if_this_changed"; pub const ATTR_THEN_THIS_WOULD_NEED: &'static str = "rustc_then_this_would_need"; pub const ATTR_PARTITION_REUSED: &'static str = "rustc_partition_reused"; -pub const ATTR_PARTITION_TRANSLATED: &'static str = "rustc_partition_translated"; +pub const ATTR_PARTITION_CODEGENED: &'static str = "rustc_partition_codegened"; pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ @@ -39,7 +39,7 @@ pub const DEP_GRAPH_ASSERT_ATTRS: &'static [&'static str] = &[ ATTR_DIRTY, ATTR_CLEAN, ATTR_PARTITION_REUSED, - ATTR_PARTITION_TRANSLATED, + ATTR_PARTITION_CODEGENED, ]; pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ @@ -49,5 +49,5 @@ pub const IGNORED_ATTRIBUTES: &'static [&'static str] = &[ ATTR_DIRTY, ATTR_CLEAN, ATTR_PARTITION_REUSED, - ATTR_PARTITION_TRANSLATED, + ATTR_PARTITION_CODEGENED, ]; diff --git a/src/librustc/infer/anon_types/mod.rs b/src/librustc/infer/anon_types/mod.rs index d681e2e3d34..4cc5e885b8a 100644 --- a/src/librustc/infer/anon_types/mod.rs +++ b/src/librustc/infer/anon_types/mod.rs @@ -614,7 +614,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for ReverseMapper<'cx, 'gcx, 'tcx> // compiler; those regions are ignored for the // outlives relation, and hence don't affect trait // selection or auto traits, and they are erased - // during trans. + // during codegen. let generics = self.tcx.generics_of(def_id); let substs = self.tcx.mk_substs(substs.substs.iter().enumerate().map( diff --git a/src/librustc/lint/context.rs b/src/librustc/lint/context.rs index f90baa2ccd9..381bb161e42 100644 --- a/src/librustc/lint/context.rs +++ b/src/librustc/lint/context.rs @@ -10,15 +10,15 @@ //! Implementation of lint checking. //! -//! The lint checking is mostly consolidated into one pass which runs just -//! before translation to LLVM bytecode. Throughout compilation, lint warnings +//! The lint checking is mostly consolidated into one pass which runs +//! after all other analyses. Throughout compilation, lint warnings //! can be added via the `add_lint` method on the Session structure. This //! requires a span and an id of the node that the lint is being added to. The //! lint isn't actually emitted at that time because it is unknown what the //! actual lint level at that location is. //! -//! To actually emit lint warnings/errors, a separate pass is used just before -//! translation. A context keeps track of the current state of all lint levels. +//! To actually emit lint warnings/errors, a separate pass is used. +//! A context keeps track of the current state of all lint levels. //! Upon entering a node of the ast which can modify the lint settings, the //! previous lint state is pushed onto a stack and the ast is then recursed //! upon. As the ast is traversed, this keeps track of the current lint level diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index d6c6f9dc0f6..7645d3486c2 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -16,15 +16,15 @@ //! other phases of the compiler, which are generally required to hold in order //! to compile the program at all. //! -//! Most lints can be written as `LintPass` instances. These run just before -//! translation to LLVM bytecode. The `LintPass`es built into rustc are defined +//! Most lints can be written as `LintPass` instances. These run after +//! all other analyses. The `LintPass`es built into rustc are defined //! within `builtin.rs`, which has further comments on how to add such a lint. //! rustc can also load user-defined lint plugins via the plugin mechanism. //! //! Some of rustc's lints are defined elsewhere in the compiler and work by //! calling `add_lint()` on the overall `Session` object. This works when //! it happens before the main lint pass, which emits the lints stored by -//! `add_lint()`. To emit lints after the main lint pass (from trans, for +//! `add_lint()`. To emit lints after the main lint pass (from codegen, for //! example) requires more effort. See `emit_lint` and `GatherNodeLevels` //! in `context.rs`. diff --git a/src/librustc/middle/dependency_format.rs b/src/librustc/middle/dependency_format.rs index 4996a6acff8..4c99b46ddff 100644 --- a/src/librustc/middle/dependency_format.rs +++ b/src/librustc/middle/dependency_format.rs @@ -109,7 +109,7 @@ fn calculate_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let sess = &tcx.sess; - if !sess.opts.output_types.should_trans() { + if !sess.opts.output_types.should_codegen() { return Vec::new(); } diff --git a/src/librustc/middle/mem_categorization.rs b/src/librustc/middle/mem_categorization.rs index a4c38333da1..e9d27c182a2 100644 --- a/src/librustc/middle/mem_categorization.rs +++ b/src/librustc/middle/mem_categorization.rs @@ -46,9 +46,9 @@ //! //! ## By-reference upvars //! -//! One part of the translation which may be non-obvious is that we translate +//! One part of the codegen which may be non-obvious is that we translate //! closure upvars into the dereference of a borrowed pointer; this more closely -//! resembles the runtime translation. So, for example, if we had: +//! resembles the runtime codegen. So, for example, if we had: //! //! let mut x = 3; //! let y = 5; diff --git a/src/librustc/middle/reachable.rs b/src/librustc/middle/reachable.rs index 0aeb15b49fb..249651ef65d 100644 --- a/src/librustc/middle/reachable.rs +++ b/src/librustc/middle/reachable.rs @@ -15,7 +15,7 @@ // makes all other generics or inline functions that it references // reachable as well. -use hir::TransFnAttrs; +use hir::CodegenFnAttrs; use hir::map as hir_map; use hir::def::Def; use hir::def_id::{DefId, CrateNum}; @@ -44,7 +44,7 @@ fn generics_require_inlining(generics: &hir::Generics) -> bool { // Returns true if the given item must be inlined because it may be // monomorphized or it was marked with `#[inline]`. This will only return // true for functions. -fn item_might_be_inlined(item: &hir::Item, attrs: TransFnAttrs) -> bool { +fn item_might_be_inlined(item: &hir::Item, attrs: CodegenFnAttrs) -> bool { if attrs.requests_inline() { return true } @@ -61,15 +61,15 @@ fn item_might_be_inlined(item: &hir::Item, attrs: TransFnAttrs) -> bool { fn method_might_be_inlined<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, impl_item: &hir::ImplItem, impl_src: DefId) -> bool { - let trans_fn_attrs = tcx.trans_fn_attrs(impl_item.hir_id.owner_def_id()); - if trans_fn_attrs.requests_inline() || + let codegen_fn_attrs = tcx.codegen_fn_attrs(impl_item.hir_id.owner_def_id()); + if codegen_fn_attrs.requests_inline() || generics_require_inlining(&impl_item.generics) { return true } if let Some(impl_node_id) = tcx.hir.as_local_node_id(impl_src) { match tcx.hir.find(impl_node_id) { Some(hir_map::NodeItem(item)) => - item_might_be_inlined(&item, trans_fn_attrs), + item_might_be_inlined(&item, codegen_fn_attrs), Some(..) | None => span_bug!(impl_item.span, "impl did is not an item") } @@ -163,7 +163,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { Some(hir_map::NodeItem(item)) => { match item.node { hir::ItemFn(..) => - item_might_be_inlined(&item, self.tcx.trans_fn_attrs(def_id)), + item_might_be_inlined(&item, self.tcx.codegen_fn_attrs(def_id)), _ => false, } } @@ -179,7 +179,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { match impl_item.node { hir::ImplItemKind::Const(..) => true, hir::ImplItemKind::Method(..) => { - let attrs = self.tcx.trans_fn_attrs(def_id); + let attrs = self.tcx.codegen_fn_attrs(def_id); if generics_require_inlining(&impl_item.generics) || attrs.requests_inline() { true @@ -233,7 +233,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { false }; let def_id = self.tcx.hir.local_def_id(item.id); - let is_extern = self.tcx.trans_fn_attrs(def_id).contains_extern_indicator(); + let is_extern = self.tcx.codegen_fn_attrs(def_id).contains_extern_indicator(); if reachable || is_extern { self.reachable_symbols.insert(search_item); } @@ -251,7 +251,7 @@ impl<'a, 'tcx> ReachableContext<'a, 'tcx> { match item.node { hir::ItemFn(.., body) => { let def_id = self.tcx.hir.local_def_id(item.id); - if item_might_be_inlined(&item, self.tcx.trans_fn_attrs(def_id)) { + if item_might_be_inlined(&item, self.tcx.codegen_fn_attrs(def_id)) { self.visit_nested_body(body); } } diff --git a/src/librustc/middle/region.rs b/src/librustc/middle/region.rs index 52217600759..71d7abed6c9 100644 --- a/src/librustc/middle/region.rs +++ b/src/librustc/middle/region.rs @@ -1279,7 +1279,7 @@ fn resolve_local<'a, 'tcx>(visitor: &mut RegionResolutionVisitor<'a, 'tcx>, loop { // Note: give all the expressions matching `ET` with the // extended temporary lifetime, not just the innermost rvalue, - // because in trans if we must compile e.g. `*rvalue()` + // because in codegen if we must compile e.g. `*rvalue()` // into a temporary, we request the temporary scope of the // outer expression. visitor.scope_tree.record_rvalue_scope(expr.hir_id.local_id, blk_scope); diff --git a/src/librustc/mir/interpret/mod.rs b/src/librustc/mir/interpret/mod.rs index afdd1c167c6..31885c1e020 100644 --- a/src/librustc/mir/interpret/mod.rs +++ b/src/librustc/mir/interpret/mod.rs @@ -249,7 +249,7 @@ pub struct Allocation { pub undef_mask: UndefMask, /// The alignment of the allocation to detect unaligned reads. pub align: Align, - /// Whether the allocation (of a static) should be put into mutable memory when translating + /// Whether the allocation (of a static) should be put into mutable memory when codegenning /// /// Only happens for `static mut` or `static` with interior mutability pub runtime_mutability: Mutability, diff --git a/src/librustc/mir/interpret/value.rs b/src/librustc/mir/interpret/value.rs index 2cd4bf9d18c..7bd84a607e7 100644 --- a/src/librustc/mir/interpret/value.rs +++ b/src/librustc/mir/interpret/value.rs @@ -75,7 +75,7 @@ impl<'tcx> ConstValue<'tcx> { /// /// For optimization of a few very common cases, there is also a representation for a pair of /// primitive values (`ByValPair`). It allows Miri to avoid making allocations for checked binary -/// operations and fat pointers. This idea was taken from rustc's trans. +/// operations and fat pointers. This idea was taken from rustc's codegen. #[derive(Clone, Copy, Debug, Eq, PartialEq, RustcEncodable, RustcDecodable, Hash)] pub enum Value { ByRef(Pointer, Align), diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index eb12444bcb4..40e9b687f0c 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -696,7 +696,7 @@ pub struct BasicBlockData<'tcx> { pub terminator: Option>, /// If true, this block lies on an unwind path. This is used - /// during trans where distinct kinds of basic blocks may be + /// during codegen where distinct kinds of basic blocks may be /// generated (particularly for MSVC cleanup). Unwind blocks must /// only branch to other unwind blocks. pub is_cleanup: bool, @@ -1614,7 +1614,7 @@ pub enum CastKind { UnsafeFnPointer, /// "Unsize" -- convert a thin-or-fat pointer to a fat pointer. - /// trans must figure out the details once full monomorphization + /// codegen must figure out the details once full monomorphization /// is known. For example, this could be used to cast from a /// `&[i32;N]` to a `&[i32]`, or a `Box` to a `Box` /// (presuming `T: Trait`). diff --git a/src/librustc/mir/mono.rs b/src/librustc/mir/mono.rs index d01059a3e01..9de9347d0ce 100644 --- a/src/librustc/mir/mono.rs +++ b/src/librustc/mir/mono.rs @@ -184,11 +184,11 @@ impl<'a, 'tcx> HashStable> for CodegenUnit<'tcx> { name.hash_stable(hcx, hasher); - let mut items: Vec<(Fingerprint, _)> = items.iter().map(|(trans_item, &attrs)| { + let mut items: Vec<(Fingerprint, _)> = items.iter().map(|(mono_item, &attrs)| { let mut hasher = StableHasher::new(); - trans_item.hash_stable(hcx, &mut hasher); - let trans_item_fingerprint = hasher.finish(); - (trans_item_fingerprint, attrs) + mono_item.hash_stable(hcx, &mut hasher); + let mono_item_fingerprint = hasher.finish(); + (mono_item_fingerprint, attrs) }).collect(); items.sort_unstable_by_key(|i| i.0); @@ -238,4 +238,3 @@ impl Stats { self.fn_stats.extend(stats.fn_stats); } } - diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 489197e0381..2344cd11f66 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -270,7 +270,7 @@ impl OutputTypes { } // True if any of the output types require codegen or linking. - pub fn should_trans(&self) -> bool { + pub fn should_codegen(&self) -> bool { self.0.keys().any(|k| match *k { OutputType::Bitcode | OutputType::Assembly @@ -1135,14 +1135,14 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "count where LLVM instrs originate"), time_llvm_passes: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, "The output of `-Z time-llvm-passes` will only reflect timings of \ - re-translated modules when used with incremental compilation" )], + re-codegened modules when used with incremental compilation" )], "measure time of each LLVM pass"), input_stats: bool = (false, parse_bool, [UNTRACKED], "gather statistics about the input"), - trans_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, - "The output of `-Z trans-stats` might not be accurate when incremental \ + codegen_stats: bool = (false, parse_bool, [UNTRACKED_WITH_WARNING(true, + "The output of `-Z codegen-stats` might not be accurate when incremental \ compilation is enabled")], - "gather trans statistics"), + "gather codegen statistics"), asm_comments: bool = (false, parse_bool, [TRACKED], "generate comments into the assembly (may change behavior)"), no_verify: bool = (false, parse_bool, [TRACKED], @@ -1183,8 +1183,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, Use with RUST_REGION_GRAPH=help for more info"), parse_only: bool = (false, parse_bool, [UNTRACKED], "parse only; do not compile, assemble, or link"), - no_trans: bool = (false, parse_bool, [TRACKED], - "run all passes except translation; no output"), + no_codegen: bool = (false, parse_bool, [TRACKED], + "run all passes except codegen; no output"), treat_err_as_bug: bool = (false, parse_bool, [TRACKED], "treat all errors that occur as bugs"), external_macro_backtrace: bool = (false, parse_bool, [UNTRACKED], @@ -1235,8 +1235,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "show spans for compiler debugging (expr|pat|ty)"), print_type_sizes: bool = (false, parse_bool, [UNTRACKED], "print layout information for each type encountered"), - print_trans_items: Option = (None, parse_opt_string, [UNTRACKED], - "print the result of the translation item collection pass"), + print_mono_items: Option = (None, parse_opt_string, [UNTRACKED], + "print the result of the monomorphization collection pass"), mir_opt_level: usize = (1, parse_uint, [TRACKED], "set the MIR optimization level (0-3, default: 1)"), mutable_noalias: bool = (false, parse_bool, [TRACKED], @@ -1244,7 +1244,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, arg_align_attributes: bool = (false, parse_bool, [TRACKED], "emit align metadata for reference arguments"), dump_mir: Option = (None, parse_opt_string, [UNTRACKED], - "dump MIR state at various points in translation"), + "dump MIR state at various points in transforms"), dump_mir_dir: String = (String::from("mir_dump"), parse_string, [UNTRACKED], "the directory the MIR is dumped into"), dump_mir_graphviz: bool = (false, parse_bool, [UNTRACKED], @@ -1296,8 +1296,8 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, "dump facts from NLL analysis into side files"), disable_nll_user_type_assert: bool = (false, parse_bool, [UNTRACKED], "disable user provided type assertion in NLL"), - trans_time_graph: bool = (false, parse_bool, [UNTRACKED], - "generate a graphical HTML report of time spent in trans and LLVM"), + codegen_time_graph: bool = (false, parse_bool, [UNTRACKED], + "generate a graphical HTML report of time spent in codegen and LLVM"), thinlto: Option = (None, parse_opt_bool, [TRACKED], "enable ThinLTO when possible"), inline_in_all_cgus: Option = (None, parse_opt_bool, [TRACKED], @@ -1309,7 +1309,7 @@ options! {DebuggingOptions, DebuggingSetter, basic_debugging_options, the max/min integer respectively, and NaN is mapped to 0"), lower_128bit_ops: Option = (None, parse_opt_bool, [TRACKED], "rewrite operators on i128 and u128 into lang item calls (typically provided \ - by compiler-builtins) so translation doesn't need to support them, + by compiler-builtins) so codegen doesn't need to support them, overriding the default for the current target"), human_readable_cgu_names: bool = (false, parse_bool, [TRACKED], "generate human-readable, predictable names for codegen units"), @@ -3047,7 +3047,7 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.input_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.trans_stats = true; + opts.debugging_opts.codegen_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.borrowck_stats = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); @@ -3093,7 +3093,7 @@ mod tests { assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.keep_ast = true; assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); - opts.debugging_opts.print_trans_items = Some(String::from("abc")); + opts.debugging_opts.print_mono_items = Some(String::from("abc")); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); opts.debugging_opts.dump_mir = Some(String::from("abc")); assert_eq!(reference.dep_tracking_hash(), opts.dep_tracking_hash()); @@ -3120,7 +3120,7 @@ mod tests { assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); opts = reference.clone(); - opts.debugging_opts.no_trans = true; + opts.debugging_opts.no_codegen = true; assert!(reference.dep_tracking_hash() != opts.dep_tracking_hash()); opts = reference.clone(); diff --git a/src/librustc/session/mod.rs b/src/librustc/session/mod.rs index bbf873290a9..6c8d4f5669e 100644 --- a/src/librustc/session/mod.rs +++ b/src/librustc/session/mod.rs @@ -98,7 +98,7 @@ pub struct Session { /// arguments passed to the compiler. Its value together with the crate-name /// forms a unique global identifier for the crate. It is used to allow /// multiple crates with the same name to coexist. See the - /// trans::back::symbol_names module for more information. + /// rustc_codegen_llvm::back::symbol_names module for more information. pub crate_disambiguator: Once, features: Once, @@ -504,8 +504,8 @@ impl Session { pub fn time_llvm_passes(&self) -> bool { self.opts.debugging_opts.time_llvm_passes } - pub fn trans_stats(&self) -> bool { - self.opts.debugging_opts.trans_stats + pub fn codegen_stats(&self) -> bool { + self.opts.debugging_opts.codegen_stats } pub fn meta_stats(&self) -> bool { self.opts.debugging_opts.meta_stats @@ -894,11 +894,11 @@ impl Session { // Why is 16 codegen units the default all the time? // // The main reason for enabling multiple codegen units by default is to - // leverage the ability for the trans backend to do translation and - // codegen in parallel. This allows us, especially for large crates, to + // leverage the ability for the codegen backend to do codegen and + // optimization in parallel. This allows us, especially for large crates, to // make good use of all available resources on the machine once we've // hit that stage of compilation. Large crates especially then often - // take a long time in trans/codegen and this helps us amortize that + // take a long time in codegen/optimization and this helps us amortize that // cost. // // Note that a high number here doesn't mean that we'll be spawning a diff --git a/src/librustc/traits/codegen/mod.rs b/src/librustc/traits/codegen/mod.rs new file mode 100644 index 00000000000..cf404202ac1 --- /dev/null +++ b/src/librustc/traits/codegen/mod.rs @@ -0,0 +1,184 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This file contains various trait resolution methods used by codegen. +// They all assume regions can be erased and monomorphic types. It +// seems likely that they should eventually be merged into more +// general routines. + +use dep_graph::{DepKind, DepTrackingMapConfig}; +use std::marker::PhantomData; +use syntax_pos::DUMMY_SP; +use infer::InferCtxt; +use syntax_pos::Span; +use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, + TraitEngine, Vtable}; +use ty::{self, Ty, TyCtxt}; +use ty::subst::{Subst, Substs}; +use ty::fold::TypeFoldable; + +/// Attempts to resolve an obligation to a vtable.. The result is +/// a shallow vtable resolution -- meaning that we do not +/// (necessarily) resolve all nested obligations on the impl. Note +/// that type check should guarantee to us that all nested +/// obligations *could be* resolved if we wanted to. +/// Assumes that this is run after the entire crate has been successfully type-checked. +pub fn codegen_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, + (param_env, trait_ref): + (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) + -> Vtable<'tcx, ()> +{ + // Remove any references to regions; this helps improve caching. + let trait_ref = ty.erase_regions(&trait_ref); + + debug!("codegen_fulfill_obligation(trait_ref={:?}, def_id={:?})", + (param_env, trait_ref), trait_ref.def_id()); + + // Do the initial selection for the obligation. This yields the + // shallow result we are looking for -- that is, what specific impl. + ty.infer_ctxt().enter(|infcx| { + let mut selcx = SelectionContext::new(&infcx); + + let obligation_cause = ObligationCause::dummy(); + let obligation = Obligation::new(obligation_cause, + param_env, + trait_ref.to_poly_trait_predicate()); + + let selection = match selcx.select(&obligation) { + Ok(Some(selection)) => selection, + Ok(None) => { + // Ambiguity can happen when monomorphizing during trans + // expands to some humongo type that never occurred + // statically -- this humongo type can then overflow, + // leading to an ambiguous result. So report this as an + // overflow bug, since I believe this is the only case + // where ambiguity can result. + bug!("Encountered ambiguity selecting `{:?}` during codegen, \ + presuming due to overflow", + trait_ref) + } + Err(e) => { + bug!("Encountered error `{:?}` selecting `{:?}` during codegen", + e, trait_ref) + } + }; + + debug!("fulfill_obligation: selection={:?}", selection); + + // Currently, we use a fulfillment context to completely resolve + // all nested obligations. This is because they can inform the + // inference of the impl's type parameters. + let mut fulfill_cx = FulfillmentContext::new(); + let vtable = selection.map(|predicate| { + debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); + fulfill_cx.register_predicate_obligation(&infcx, predicate); + }); + let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable); + + info!("Cache miss: {:?} => {:?}", trait_ref, vtable); + vtable + }) +} + +impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { + /// Monomorphizes a type from the AST by first applying the + /// in-scope substitutions and then normalizing any associated + /// types. + pub fn subst_and_normalize_erasing_regions( + self, + param_substs: &Substs<'tcx>, + param_env: ty::ParamEnv<'tcx>, + value: &T + ) -> T + where + T: TypeFoldable<'tcx>, + { + debug!( + "subst_and_normalize_erasing_regions(\ + param_substs={:?}, \ + value={:?}, \ + param_env={:?})", + param_substs, + value, + param_env, + ); + let substituted = value.subst(self, param_substs); + self.normalize_erasing_regions(param_env, substituted) + } +} + +// Implement DepTrackingMapConfig for `trait_cache` +pub struct TraitSelectionCache<'tcx> { + data: PhantomData<&'tcx ()> +} + +impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { + type Key = (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>); + type Value = Vtable<'tcx, ()>; + fn to_dep_kind() -> DepKind { + DepKind::TraitSelect + } +} + +// # Global Cache + +pub struct ProjectionCache<'gcx> { + data: PhantomData<&'gcx ()> +} + +impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> { + type Key = Ty<'gcx>; + type Value = Ty<'gcx>; + fn to_dep_kind() -> DepKind { + DepKind::TraitSelect + } +} + +impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { + /// Finishes processes any obligations that remain in the + /// fulfillment context, and then returns the result with all type + /// variables removed and regions erased. Because this is intended + /// for use after type-check has completed, if any errors occur, + /// it will panic. It is used during normalization and other cases + /// where processing the obligations in `fulfill_cx` may cause + /// type inference variables that appear in `result` to be + /// unified, and hence we need to process those obligations to get + /// the complete picture of the type. + fn drain_fulfillment_cx_or_panic(&self, + span: Span, + fulfill_cx: &mut FulfillmentContext<'tcx>, + result: &T) + -> T::Lifted + where T: TypeFoldable<'tcx> + ty::Lift<'gcx> + { + debug!("drain_fulfillment_cx_or_panic()"); + + // In principle, we only need to do this so long as `result` + // contains unbound type parameters. It could be a slight + // optimization to stop iterating early. + match fulfill_cx.select_all_or_error(self) { + Ok(()) => { } + Err(errors) => { + span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", + errors); + } + } + + let result = self.resolve_type_vars_if_possible(result); + let result = self.tcx.erase_regions(&result); + + match self.tcx.lift_to_global(&result) { + Some(result) => result, + None => { + span_bug!(span, "Uninferred types/regions in `{:?}`", result); + } + } + } +} diff --git a/src/librustc/traits/mod.rs b/src/librustc/traits/mod.rs index f4f0c47899d..b5115eab7dc 100644 --- a/src/librustc/traits/mod.rs +++ b/src/librustc/traits/mod.rs @@ -64,7 +64,7 @@ mod on_unimplemented; mod select; mod specialize; mod structural_impls; -pub mod trans; +pub mod codegen; mod util; pub mod query; @@ -473,8 +473,8 @@ pub enum Vtable<'tcx, N> { /// /// The type parameter `N` indicates the type used for "nested /// obligations" that are required by the impl. During type check, this -/// is `Obligation`, as one might expect. During trans, however, this -/// is `()`, because trans only requires a shallow resolution of an +/// is `Obligation`, as one might expect. During codegen, however, this +/// is `()`, because codegen only requires a shallow resolution of an /// impl, and nested obligations are satisfied later. #[derive(Clone, PartialEq, Eq, RustcEncodable, RustcDecodable)] pub struct VtableImplData<'tcx, N> { @@ -856,7 +856,7 @@ fn vtable_methods<'a, 'tcx>( // It's possible that the method relies on where clauses that // do not hold for this particular set of type parameters. // Note that this method could then never be called, so we - // do not want to try and trans it, in that case (see #23435). + // do not want to try and codegen it, in that case (see #23435). let predicates = tcx.predicates_of(def_id).instantiate_own(tcx, substs); if !normalize_and_test_predicates(tcx, predicates.predicates) { debug!("vtable_methods: predicates do not hold"); @@ -992,7 +992,7 @@ pub fn provide(providers: &mut ty::maps::Providers) { is_object_safe: object_safety::is_object_safe_provider, specialization_graph_of: specialize::specialization_graph_provider, specializes: specialize::specializes, - trans_fulfill_obligation: trans::trans_fulfill_obligation, + codegen_fulfill_obligation: codegen::codegen_fulfill_obligation, vtable_methods, substitute_normalize_and_test_predicates, ..*providers diff --git a/src/librustc/traits/project.rs b/src/librustc/traits/project.rs index bfa32f8e7fa..270e06a872b 100644 --- a/src/librustc/traits/project.rs +++ b/src/librustc/traits/project.rs @@ -65,7 +65,7 @@ pub enum Reveal { /// } UserFacing, - /// At trans time, all monomorphic projections will succeed. + /// At codegen time, all monomorphic projections will succeed. /// Also, `impl Trait` is normalized to the concrete type, /// which has to be already collected by type-checking. /// @@ -346,7 +346,7 @@ impl<'a, 'b, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for AssociatedTypeNormalizer<'a, let ty = ty.super_fold_with(self); match ty.sty { ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*) - // Only normalize `impl Trait` after type-checking, usually in trans. + // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal { Reveal::UserFacing => ty, @@ -1054,7 +1054,7 @@ fn assemble_candidates_from_impls<'cx, 'gcx, 'tcx>( super::VtableImpl(impl_data) => { // We have to be careful when projecting out of an // impl because of specialization. If we are not in - // trans (i.e., projection mode is not "any"), and the + // codegen (i.e., projection mode is not "any"), and the // impl's type is declared as default, then we disable // projection (even if the trait ref is fully // monomorphic). In the case where trait ref is not diff --git a/src/librustc/traits/query/normalize.rs b/src/librustc/traits/query/normalize.rs index f074e061653..1e9e9c056c9 100644 --- a/src/librustc/traits/query/normalize.rs +++ b/src/librustc/traits/query/normalize.rs @@ -104,7 +104,7 @@ impl<'cx, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for QueryNormalizer<'cx, 'gcx, 'tcx match ty.sty { ty::TyAnon(def_id, substs) if !substs.has_escaping_regions() => { // (*) - // Only normalize `impl Trait` after type-checking, usually in trans. + // Only normalize `impl Trait` after type-checking, usually in codegen. match self.param_env.reveal { Reveal::UserFacing => ty, diff --git a/src/librustc/traits/query/normalize_erasing_regions.rs b/src/librustc/traits/query/normalize_erasing_regions.rs index a9734e9c298..1cb96a3e33f 100644 --- a/src/librustc/traits/query/normalize_erasing_regions.rs +++ b/src/librustc/traits/query/normalize_erasing_regions.rs @@ -57,7 +57,7 @@ impl<'cx, 'tcx> TyCtxt<'cx, 'tcx, 'tcx> { /// /// NB. Currently, higher-ranked type bounds inhibit /// normalization. Therefore, each time we erase them in - /// translation, we need to normalize the contents. + /// codegen, we need to normalize the contents. pub fn normalize_erasing_late_bound_regions( self, param_env: ty::ParamEnv<'tcx>, diff --git a/src/librustc/traits/structural_impls.rs b/src/librustc/traits/structural_impls.rs index b9593047af4..2e2f7a2ad31 100644 --- a/src/librustc/traits/structural_impls.rs +++ b/src/librustc/traits/structural_impls.rs @@ -275,7 +275,7 @@ impl<'a, 'tcx> Lift<'tcx> for traits::ObligationCause<'a> { } } -// For trans only. +// For codegen only. impl<'a, 'tcx> Lift<'tcx> for traits::Vtable<'a, ()> { type Lifted = traits::Vtable<'tcx, ()>; fn lift_to_tcx<'b, 'gcx>(&self, tcx: TyCtxt<'b, 'gcx, 'tcx>) -> Option { diff --git a/src/librustc/traits/trans/mod.rs b/src/librustc/traits/trans/mod.rs deleted file mode 100644 index 31e851126d7..00000000000 --- a/src/librustc/traits/trans/mod.rs +++ /dev/null @@ -1,184 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This file contains various trait resolution methods used by trans. -// They all assume regions can be erased and monomorphic types. It -// seems likely that they should eventually be merged into more -// general routines. - -use dep_graph::{DepKind, DepTrackingMapConfig}; -use std::marker::PhantomData; -use syntax_pos::DUMMY_SP; -use infer::InferCtxt; -use syntax_pos::Span; -use traits::{FulfillmentContext, Obligation, ObligationCause, SelectionContext, - TraitEngine, Vtable}; -use ty::{self, Ty, TyCtxt}; -use ty::subst::{Subst, Substs}; -use ty::fold::TypeFoldable; - -/// Attempts to resolve an obligation to a vtable.. The result is -/// a shallow vtable resolution -- meaning that we do not -/// (necessarily) resolve all nested obligations on the impl. Note -/// that type check should guarantee to us that all nested -/// obligations *could be* resolved if we wanted to. -/// Assumes that this is run after the entire crate has been successfully type-checked. -pub fn trans_fulfill_obligation<'a, 'tcx>(ty: TyCtxt<'a, 'tcx, 'tcx>, - (param_env, trait_ref): - (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) - -> Vtable<'tcx, ()> -{ - // Remove any references to regions; this helps improve caching. - let trait_ref = ty.erase_regions(&trait_ref); - - debug!("trans::fulfill_obligation(trait_ref={:?}, def_id={:?})", - (param_env, trait_ref), trait_ref.def_id()); - - // Do the initial selection for the obligation. This yields the - // shallow result we are looking for -- that is, what specific impl. - ty.infer_ctxt().enter(|infcx| { - let mut selcx = SelectionContext::new(&infcx); - - let obligation_cause = ObligationCause::dummy(); - let obligation = Obligation::new(obligation_cause, - param_env, - trait_ref.to_poly_trait_predicate()); - - let selection = match selcx.select(&obligation) { - Ok(Some(selection)) => selection, - Ok(None) => { - // Ambiguity can happen when monomorphizing during trans - // expands to some humongo type that never occurred - // statically -- this humongo type can then overflow, - // leading to an ambiguous result. So report this as an - // overflow bug, since I believe this is the only case - // where ambiguity can result. - bug!("Encountered ambiguity selecting `{:?}` during trans, \ - presuming due to overflow", - trait_ref) - } - Err(e) => { - bug!("Encountered error `{:?}` selecting `{:?}` during trans", - e, trait_ref) - } - }; - - debug!("fulfill_obligation: selection={:?}", selection); - - // Currently, we use a fulfillment context to completely resolve - // all nested obligations. This is because they can inform the - // inference of the impl's type parameters. - let mut fulfill_cx = FulfillmentContext::new(); - let vtable = selection.map(|predicate| { - debug!("fulfill_obligation: register_predicate_obligation {:?}", predicate); - fulfill_cx.register_predicate_obligation(&infcx, predicate); - }); - let vtable = infcx.drain_fulfillment_cx_or_panic(DUMMY_SP, &mut fulfill_cx, &vtable); - - info!("Cache miss: {:?} => {:?}", trait_ref, vtable); - vtable - }) -} - -impl<'a, 'tcx> TyCtxt<'a, 'tcx, 'tcx> { - /// Monomorphizes a type from the AST by first applying the - /// in-scope substitutions and then normalizing any associated - /// types. - pub fn subst_and_normalize_erasing_regions( - self, - param_substs: &Substs<'tcx>, - param_env: ty::ParamEnv<'tcx>, - value: &T - ) -> T - where - T: TypeFoldable<'tcx>, - { - debug!( - "subst_and_normalize_erasing_regions(\ - param_substs={:?}, \ - value={:?}, \ - param_env={:?})", - param_substs, - value, - param_env, - ); - let substituted = value.subst(self, param_substs); - self.normalize_erasing_regions(param_env, substituted) - } -} - -// Implement DepTrackingMapConfig for `trait_cache` -pub struct TraitSelectionCache<'tcx> { - data: PhantomData<&'tcx ()> -} - -impl<'tcx> DepTrackingMapConfig for TraitSelectionCache<'tcx> { - type Key = (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>); - type Value = Vtable<'tcx, ()>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - -// # Global Cache - -pub struct ProjectionCache<'gcx> { - data: PhantomData<&'gcx ()> -} - -impl<'gcx> DepTrackingMapConfig for ProjectionCache<'gcx> { - type Key = Ty<'gcx>; - type Value = Ty<'gcx>; - fn to_dep_kind() -> DepKind { - DepKind::TraitSelect - } -} - -impl<'a, 'gcx, 'tcx> InferCtxt<'a, 'gcx, 'tcx> { - /// Finishes processes any obligations that remain in the - /// fulfillment context, and then returns the result with all type - /// variables removed and regions erased. Because this is intended - /// for use after type-check has completed, if any errors occur, - /// it will panic. It is used during normalization and other cases - /// where processing the obligations in `fulfill_cx` may cause - /// type inference variables that appear in `result` to be - /// unified, and hence we need to process those obligations to get - /// the complete picture of the type. - fn drain_fulfillment_cx_or_panic(&self, - span: Span, - fulfill_cx: &mut FulfillmentContext<'tcx>, - result: &T) - -> T::Lifted - where T: TypeFoldable<'tcx> + ty::Lift<'gcx> - { - debug!("drain_fulfillment_cx_or_panic()"); - - // In principle, we only need to do this so long as `result` - // contains unbound type parameters. It could be a slight - // optimization to stop iterating early. - match fulfill_cx.select_all_or_error(self) { - Ok(()) => { } - Err(errors) => { - span_bug!(span, "Encountered errors `{:?}` resolving bounds after type-checking", - errors); - } - } - - let result = self.resolve_type_vars_if_possible(result); - let result = self.tcx.erase_regions(&result); - - match self.tcx.lift_to_global(&result) { - Some(result) => result, - None => { - span_bug!(span, "Uninferred types/regions in `{:?}`", result); - } - } - } -} diff --git a/src/librustc/ty/adjustment.rs b/src/librustc/ty/adjustment.rs index a0c31e8b509..3263da8fda3 100644 --- a/src/librustc/ty/adjustment.rs +++ b/src/librustc/ty/adjustment.rs @@ -91,8 +91,8 @@ pub enum Adjust<'tcx> { /// pointers. We don't store the details of how the transform is /// done (in fact, we don't know that, because it might depend on /// the precise type parameters). We just store the target - /// type. Trans figures out what has to be done at monomorphization - /// time based on the precise source/target type at hand. + /// type. Codegen backends and miri figure out what has to be done + /// based on the precise source/target type at hand. Unsize, } diff --git a/src/librustc/ty/cast.rs b/src/librustc/ty/cast.rs index 908335424e7..7593d4ed24e 100644 --- a/src/librustc/ty/cast.rs +++ b/src/librustc/ty/cast.rs @@ -9,7 +9,7 @@ // except according to those terms. // Helpers for handling cast expressions, used in both -// typeck and trans. +// typeck and codegen. use ty::{self, Ty}; diff --git a/src/librustc/ty/context.rs b/src/librustc/ty/context.rs index 3c345fcd9ee..ee55b8dd767 100644 --- a/src/librustc/ty/context.rs +++ b/src/librustc/ty/context.rs @@ -401,7 +401,7 @@ pub struct TypeckTables<'tcx> { /// For each fn, records the "liberated" types of its arguments /// and return type. Liberated means that all bound regions /// (including late-bound regions) are replaced with free - /// equivalents. This table is not used in trans (since regions + /// equivalents. This table is not used in codegen (since regions /// are erased there) and hence is not serialized to metadata. liberated_fn_sigs: ItemLocalMap>, @@ -921,7 +921,7 @@ pub struct GlobalCtxt<'tcx> { /// A general purpose channel to throw data out the back towards LLVM worker /// threads. /// - /// This is intended to only get used during the trans phase of the compiler + /// This is intended to only get used during the codegen phase of the compiler /// when satisfying the query for a particular codegen unit. Internally in /// the query it'll send data along this channel to get processed later. pub tx_to_llvm_workers: Lock>>, diff --git a/src/librustc/ty/erase_regions.rs b/src/librustc/ty/erase_regions.rs index 4f8fca67949..f483b4c174a 100644 --- a/src/librustc/ty/erase_regions.rs +++ b/src/librustc/ty/erase_regions.rs @@ -68,7 +68,7 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 't // // Note that we *CAN* replace early-bound regions -- the // type system never "sees" those, they get substituted - // away. In trans, they will always be erased to 'erased + // away. In codegen, they will always be erased to 'erased // whenever a substitution occurs. match *r { ty::ReLateBound(..) => r, @@ -76,4 +76,3 @@ impl<'a, 'gcx, 'tcx> TypeFolder<'gcx, 'tcx> for RegionEraserVisitor<'a, 'gcx, 't } } } - diff --git a/src/librustc/ty/fold.rs b/src/librustc/ty/fold.rs index 250f33d9dba..8aca7177d55 100644 --- a/src/librustc/ty/fold.rs +++ b/src/librustc/ty/fold.rs @@ -424,7 +424,7 @@ impl<'a, 'gcx, 'tcx> TyCtxt<'a, 'gcx, 'tcx> { collector.regions } - /// Replace any late-bound regions bound in `value` with `'erased`. Useful in trans but also + /// Replace any late-bound regions bound in `value` with `'erased`. Useful in codegen but also /// method lookup and a few other places where precise region relationships are not required. pub fn erase_late_bound_regions(self, value: &Binder) -> T where T : TypeFoldable<'tcx> diff --git a/src/librustc/ty/instance.rs b/src/librustc/ty/instance.rs index e7b71ca2b22..e97f782fccf 100644 --- a/src/librustc/ty/instance.rs +++ b/src/librustc/ty/instance.rs @@ -104,13 +104,13 @@ impl<'tcx> InstanceDef<'tcx> { return true } if let ty::InstanceDef::DropGlue(..) = *self { - // Drop glue wants to be instantiated at every translation + // Drop glue wants to be instantiated at every codegen // unit, but without an #[inline] hint. We should make this // available to normal end-users. return true } - let trans_fn_attrs = tcx.trans_fn_attrs(self.def_id()); - trans_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id()) + let codegen_fn_attrs = tcx.codegen_fn_attrs(self.def_id()); + codegen_fn_attrs.requests_inline() || tcx.is_const_fn(self.def_id()) } } @@ -145,7 +145,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { pub fn new(def_id: DefId, substs: &'tcx Substs<'tcx>) -> Instance<'tcx> { assert!(!substs.has_escaping_regions(), - "substs of instance {:?} not normalized for trans: {:?}", + "substs of instance {:?} not normalized for codegen: {:?}", def_id, substs); Instance { def: InstanceDef::Item(def_id), substs: substs } } @@ -175,7 +175,7 @@ impl<'a, 'b, 'tcx> Instance<'tcx> { /// `RevealMode` in the parameter environment.) /// /// Presuming that coherence and type-check have succeeded, if this method is invoked - /// in a monomorphic context (i.e., like during trans), then it is guaranteed to return + /// in a monomorphic context (i.e., like during codegen), then it is guaranteed to return /// `Some`. pub fn resolve(tcx: TyCtxt<'a, 'tcx, 'tcx>, param_env: ty::ParamEnv<'tcx>, @@ -259,7 +259,7 @@ fn resolve_associated_item<'a, 'tcx>( def_id, trait_id, rcvr_substs); let trait_ref = ty::TraitRef::from_method(tcx, trait_id, rcvr_substs); - let vtbl = tcx.trans_fulfill_obligation((param_env, ty::Binder::bind(trait_ref))); + let vtbl = tcx.codegen_fulfill_obligation((param_env, ty::Binder::bind(trait_ref))); // Now that we know which impl is being used, we can dispatch to // the actual function: @@ -321,7 +321,7 @@ fn needs_fn_once_adapter_shim<'a, 'tcx>(actual_closure_kind: ty::ClosureKind, } (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are + // `fn(&mut self, ...)`. In fact, at codegen time, these are // basically the same thing, so we can just return llfn. Ok(false) } @@ -334,7 +334,7 @@ fn needs_fn_once_adapter_shim<'a, 'tcx>(actual_closure_kind: ty::ClosureKind, // fn call_once(self, ...) { call_mut(&self, ...) } // fn call_once(mut self, ...) { call_mut(&mut self, ...) } // - // These are both the same at trans time. + // These are both the same at codegen time. Ok(true) } (ty::ClosureKind::FnMut, _) | diff --git a/src/librustc/ty/layout.rs b/src/librustc/ty/layout.rs index bbfc6d883e9..dbc32f4dbdd 100644 --- a/src/librustc/ty/layout.rs +++ b/src/librustc/ty/layout.rs @@ -949,14 +949,14 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // because this discriminant will be loaded, and then stored into variable of // type calculated by typeck. Consider such case (a bug): typeck decided on // byte-sized discriminant, but layout thinks we need a 16-bit to store all - // discriminant values. That would be a bug, because then, in trans, in order + // discriminant values. That would be a bug, because then, in codegen, in order // to store this 16-bit discriminant into 8-bit sized temporary some of the // space necessary to represent would have to be discarded (or layout is wrong // on thinking it needs 16 bits) bug!("layout decided on a larger discriminant type ({:?}) than typeck ({:?})", min_ity, typeck_ity); // However, it is fine to make discr type however large (as an optimisation) - // after this point – we’ll just truncate the value we load in trans. + // after this point – we’ll just truncate the value we load in codegen. } // Check to see if we should use a different type for the @@ -1121,7 +1121,7 @@ impl<'a, 'tcx> LayoutCx<'tcx, TyCtxt<'a, 'tcx, 'tcx>> { // If we are running with `-Zprint-type-sizes`, record layouts for // dumping later. Ignore layouts that are done with non-empty // environments or non-monomorphic layouts, as the user only wants - // to see the stuff resulting from the final trans session. + // to see the stuff resulting from the final codegen session. if !self.tcx.sess.opts.debugging_opts.print_type_sizes || layout.ty.has_param_types() || diff --git a/src/librustc/ty/maps/config.rs b/src/librustc/ty/maps/config.rs index ad48519e136..19c97a918bb 100644 --- a/src/librustc/ty/maps/config.rs +++ b/src/librustc/ty/maps/config.rs @@ -349,7 +349,7 @@ impl<'tcx> QueryDescription<'tcx> for queries::is_mir_available<'tcx> { } } -impl<'tcx> QueryDescription<'tcx> for queries::trans_fulfill_obligation<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::codegen_fulfill_obligation<'tcx> { fn describe(tcx: TyCtxt, key: (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> String { format!("checking if `{}` fulfills its obligations", tcx.item_path_str(key.1.def_id())) } @@ -637,9 +637,9 @@ impl<'tcx> QueryDescription<'tcx> for queries::exported_symbols<'tcx> { } } -impl<'tcx> QueryDescription<'tcx> for queries::collect_and_partition_translation_items<'tcx> { +impl<'tcx> QueryDescription<'tcx> for queries::collect_and_partition_mono_items<'tcx> { fn describe(_tcx: TyCtxt, _: CrateNum) -> String { - format!("collect_and_partition_translation_items") + format!("collect_and_partition_mono_items") } } @@ -795,5 +795,5 @@ impl_disk_cacheable_query!(def_symbol_name, |_| true); impl_disk_cacheable_query!(type_of, |def_id| def_id.is_local()); impl_disk_cacheable_query!(predicates_of, |def_id| def_id.is_local()); impl_disk_cacheable_query!(used_trait_imports, |def_id| def_id.is_local()); -impl_disk_cacheable_query!(trans_fn_attrs, |_| true); +impl_disk_cacheable_query!(codegen_fn_attrs, |_| true); impl_disk_cacheable_query!(specialization_graph_of, |_| true); diff --git a/src/librustc/ty/maps/mod.rs b/src/librustc/ty/maps/mod.rs index 6e419627dd8..6556e47720c 100644 --- a/src/librustc/ty/maps/mod.rs +++ b/src/librustc/ty/maps/mod.rs @@ -11,7 +11,7 @@ use dep_graph::{DepConstructor, DepNode}; use hir::def_id::{CrateNum, DefId, DefIndex}; use hir::def::{Def, Export}; -use hir::{self, TraitCandidate, ItemLocalId, TransFnAttrs}; +use hir::{self, TraitCandidate, ItemLocalId, CodegenFnAttrs}; use hir::svh::Svh; use infer::canonical::{self, Canonical}; use lint; @@ -181,7 +181,7 @@ define_maps! { <'tcx> [] fn mir_validated: MirValidated(DefId) -> &'tcx Steal>, /// MIR after our optimization passes have run. This is MIR that is ready - /// for trans. This is also the only query that can fetch non-local MIR, at present. + /// for codegen. This is also the only query that can fetch non-local MIR, at present. [] fn optimized_mir: MirOptimized(DefId) -> &'tcx mir::Mir<'tcx>, /// The result of unsafety-checking this def-id. @@ -255,7 +255,7 @@ define_maps! { <'tcx> [] fn lookup_stability: LookupStability(DefId) -> Option<&'tcx attr::Stability>, [] fn lookup_deprecation_entry: LookupDeprecationEntry(DefId) -> Option, [] fn item_attrs: ItemAttrs(DefId) -> Lrc<[ast::Attribute]>, - [] fn trans_fn_attrs: trans_fn_attrs(DefId) -> TransFnAttrs, + [] fn codegen_fn_attrs: codegen_fn_attrs(DefId) -> CodegenFnAttrs, [] fn fn_arg_names: FnArgNames(DefId) -> Vec, /// Gets the rendered value of the specified constant or associated constant. /// Used by rustdoc. @@ -268,7 +268,7 @@ define_maps! { <'tcx> [] fn vtable_methods: vtable_methods_node(ty::PolyTraitRef<'tcx>) -> Lrc)>>>, - [] fn trans_fulfill_obligation: fulfill_obligation_dep_node( + [] fn codegen_fulfill_obligation: fulfill_obligation_dep_node( (ty::ParamEnv<'tcx>, ty::PolyTraitRef<'tcx>)) -> Vtable<'tcx, ()>, [] fn trait_impls_of: TraitImpls(DefId) -> Lrc, [] fn specialization_graph_of: SpecializationGraph(DefId) -> Lrc, @@ -402,10 +402,10 @@ define_maps! { <'tcx> [] fn exported_symbols: ExportedSymbols(CrateNum) -> Arc, SymbolExportLevel)>>, - [] fn collect_and_partition_translation_items: - collect_and_partition_translation_items_node(CrateNum) + [] fn collect_and_partition_mono_items: + collect_and_partition_mono_items_node(CrateNum) -> (Arc, Arc>>>), - [] fn is_translated_item: IsTranslatedItem(DefId) -> bool, + [] fn is_codegened_item: IsCodegenedItem(DefId) -> bool, [] fn codegen_unit: CodegenUnit(InternedString) -> Arc>, [] fn compile_codegen_unit: CompileCodegenUnit(InternedString) -> Stats, [] fn output_filenames: output_filenames_node(CrateNum) @@ -475,8 +475,8 @@ fn features_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::Features } -fn trans_fn_attrs<'tcx>(id: DefId) -> DepConstructor<'tcx> { - DepConstructor::TransFnAttrs { 0: id } +fn codegen_fn_attrs<'tcx>(id: DefId) -> DepConstructor<'tcx> { + DepConstructor::CodegenFnAttrs { 0: id } } fn erase_regions_ty<'tcx>(ty: Ty<'tcx>) -> DepConstructor<'tcx> { @@ -609,8 +609,8 @@ fn all_traits_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { DepConstructor::AllTraits } -fn collect_and_partition_translation_items_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { - DepConstructor::CollectAndPartitionTranslationItems +fn collect_and_partition_mono_items_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { + DepConstructor::CollectAndPartitionMonoItems } fn output_filenames_node<'tcx>(_: CrateNum) -> DepConstructor<'tcx> { diff --git a/src/librustc/ty/maps/on_disk_cache.rs b/src/librustc/ty/maps/on_disk_cache.rs index cea2a03fd53..9ca90a06c4e 100644 --- a/src/librustc/ty/maps/on_disk_cache.rs +++ b/src/librustc/ty/maps/on_disk_cache.rs @@ -224,7 +224,7 @@ impl<'sess> OnDiskCache<'sess> { encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; - encode_query_results::(tcx, enc, qri)?; + encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; @@ -234,7 +234,7 @@ impl<'sess> OnDiskCache<'sess> { encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; - encode_query_results::(tcx, enc, qri)?; + encode_query_results::(tcx, enc, qri)?; encode_query_results::(tcx, enc, qri)?; // const eval is special, it only encodes successfully evaluated constants diff --git a/src/librustc/ty/maps/plumbing.rs b/src/librustc/ty/maps/plumbing.rs index 65dcb7311d3..065b7939ac3 100644 --- a/src/librustc/ty/maps/plumbing.rs +++ b/src/librustc/ty/maps/plumbing.rs @@ -871,7 +871,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, // Since we cannot reconstruct the query key of a DepNode::CodegenUnit, we // would always end up having to evaluate the first caller of the // `codegen_unit` query that *is* reconstructible. This might very well be - // the `compile_codegen_unit` query, thus re-translating the whole CGU just + // the `compile_codegen_unit` query, thus re-codegenning the whole CGU just // to re-trigger calling the `codegen_unit` query with the right key. At // that point we would already have re-done all the work we are trying to // avoid doing in the first place. @@ -1046,7 +1046,7 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, } DepKind::IsMirAvailable => { force!(is_mir_available, def_id!()); } DepKind::ItemAttrs => { force!(item_attrs, def_id!()); } - DepKind::TransFnAttrs => { force!(trans_fn_attrs, def_id!()); } + DepKind::CodegenFnAttrs => { force!(codegen_fn_attrs, def_id!()); } DepKind::FnArgNames => { force!(fn_arg_names, def_id!()); } DepKind::RenderedConst => { force!(rendered_const, def_id!()); } DepKind::DylibDepFormats => { force!(dylib_dependency_formats, krate!()); } @@ -1121,10 +1121,10 @@ pub fn force_from_dep_node<'a, 'gcx, 'lcx>(tcx: TyCtxt<'a, 'gcx, 'lcx>, DepKind::AllTraits => { force!(all_traits, LOCAL_CRATE); } DepKind::AllCrateNums => { force!(all_crate_nums, LOCAL_CRATE); } DepKind::ExportedSymbols => { force!(exported_symbols, krate!()); } - DepKind::CollectAndPartitionTranslationItems => { - force!(collect_and_partition_translation_items, LOCAL_CRATE); + DepKind::CollectAndPartitionMonoItems => { + force!(collect_and_partition_mono_items, LOCAL_CRATE); } - DepKind::IsTranslatedItem => { force!(is_translated_item, def_id!()); } + DepKind::IsCodegenedItem => { force!(is_codegened_item, def_id!()); } DepKind::OutputFilenames => { force!(output_filenames, LOCAL_CRATE); } DepKind::TargetFeaturesWhitelist => { force!(target_features_whitelist, LOCAL_CRATE); } @@ -1207,6 +1207,6 @@ impl_load_from_cache!( GenericsOfItem => generics_of, PredicatesOfItem => predicates_of, UsedTraitImports => used_trait_imports, - TransFnAttrs => trans_fn_attrs, + CodegenFnAttrs => codegen_fn_attrs, SpecializationGraph => specialization_graph_of, ); diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index eb638d7c9a1..ce7feae0719 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -118,7 +118,7 @@ mod sty; // Data types /// The complete set of all analyses described in this module. This is -/// produced by the driver and fed to trans and later passes. +/// produced by the driver and fed to codegen and later passes. /// /// NB: These contents are being migrated into queries using the /// *on-demand* infrastructure. @@ -1426,7 +1426,7 @@ pub struct ParamEnv<'tcx> { /// into Obligations, and elaborated and normalized. pub caller_bounds: &'tcx Slice>, - /// Typically, this is `Reveal::UserFacing`, but during trans we + /// Typically, this is `Reveal::UserFacing`, but during codegen we /// want `Reveal::All` -- note that this is always paired with an /// empty environment. To get that, use `ParamEnv::reveal()`. pub reveal: traits::Reveal, @@ -1444,7 +1444,7 @@ impl<'tcx> ParamEnv<'tcx> { /// Construct a trait environment with no where clauses in scope /// where the values of all `impl Trait` and other hidden types /// are revealed. This is suitable for monomorphized, post-typeck - /// environments like trans or doing optimizations. + /// environments like codegen or doing optimizations. /// /// NB. If you want to have predicates in scope, use `ParamEnv::new`, /// or invoke `param_env.with_reveal_all()`. @@ -1462,7 +1462,7 @@ impl<'tcx> ParamEnv<'tcx> { /// Returns a new parameter environment with the same clauses, but /// which "reveals" the true results of projections in all cases /// (even for associated types that are specializable). This is - /// the desired behavior during trans and certain other special + /// the desired behavior during codegen and certain other special /// contexts; normally though we want to use `Reveal::UserFacing`, /// which is the default. pub fn with_reveal_all(self) -> Self { diff --git a/src/librustc/ty/sty.rs b/src/librustc/ty/sty.rs index 5c0217fc3f5..4493c668593 100644 --- a/src/librustc/ty/sty.rs +++ b/src/librustc/ty/sty.rs @@ -243,7 +243,7 @@ pub enum TypeVariants<'tcx> { /// out later. /// /// All right, you say, but why include the type parameters from the -/// original function then? The answer is that trans may need them +/// original function then? The answer is that codegen may need them /// when monomorphizing, and they may not appear in the upvars. A /// closure could capture no variables but still make use of some /// in-scope type parameter with a bound (e.g., if our example above @@ -273,7 +273,7 @@ pub struct ClosureSubsts<'tcx> { /// Lifetime and type parameters from the enclosing function, /// concatenated with the types of the upvars. /// - /// These are separated out because trans wants to pass them around + /// These are separated out because codegen wants to pass them around /// when monomorphizing. pub substs: &'tcx Substs<'tcx>, } @@ -1093,7 +1093,7 @@ pub enum RegionKind { /// variable with no constraints. ReEmpty, - /// Erased region, used by trait selection, in MIR and during trans. + /// Erased region, used by trait selection, in MIR and during codegen. ReErased, /// These are regions bound in the "defining type" for a diff --git a/src/librustc/util/ppaux.rs b/src/librustc/util/ppaux.rs index eaae874635f..c66dd79cbf5 100644 --- a/src/librustc/util/ppaux.rs +++ b/src/librustc/util/ppaux.rs @@ -1135,7 +1135,7 @@ define_print! { })? } else { // cross-crate closure types should only be - // visible in trans bug reports, I imagine. + // visible in codegen bug reports, I imagine. write!(f, "@{:?}", did)?; let mut sep = " "; for (index, upvar_ty) in upvar_tys.enumerate() { @@ -1175,7 +1175,7 @@ define_print! { })? } else { // cross-crate closure types should only be - // visible in trans bug reports, I imagine. + // visible in codegen bug reports, I imagine. write!(f, "@{:?}", did)?; let mut sep = " "; for (index, upvar_ty) in upvar_tys.enumerate() { diff --git a/src/librustc_borrowck/borrowck/README.md b/src/librustc_borrowck/borrowck/README.md index 29f03c06ab7..6fc0ed47b80 100644 --- a/src/librustc_borrowck/borrowck/README.md +++ b/src/librustc_borrowck/borrowck/README.md @@ -1126,7 +1126,7 @@ fn foo(a: [D; 10], b: [D; 10], i: i32, t: bool) -> D { } ``` -There are a number of ways that the trans backend could choose to +There are a number of ways that the codegen backend could choose to compile this (e.g. a `[bool; 10]` array for each such moved array; or an `Option` for each moved array). From the viewpoint of the borrow-checker, the important thing is to record what kind of fragment diff --git a/src/librustc_codegen_llvm/Cargo.toml b/src/librustc_codegen_llvm/Cargo.toml new file mode 100644 index 00000000000..7cf0a1c1bec --- /dev/null +++ b/src/librustc_codegen_llvm/Cargo.toml @@ -0,0 +1,49 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_codegen_llvm" +version = "0.0.0" + +[lib] +name = "rustc_codegen_llvm" +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +bitflags = "1.0.1" +cc = "1.0.1" +flate2 = "1.0" +jobserver = "0.1.5" +libc = "0.2" +log = "0.4" +num_cpus = "1.0" +rustc = { path = "../librustc" } +rustc-demangle = "0.1.4" +rustc_allocator = { path = "../librustc_allocator" } +rustc_apfloat = { path = "../librustc_apfloat" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_errors = { path = "../librustc_errors" } +rustc_incremental = { path = "../librustc_incremental" } +rustc_llvm = { path = "../librustc_llvm" } +rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } +rustc_mir = { path = "../librustc_mir" } +serialize = { path = "../libserialize" } +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +tempdir = "0.3" + +# not actually used but needed to make sure we enable the same feature set as +# winapi used in librustc +env_logger = { version = "0.5", default-features = false } + +[features] +# Used to communicate the feature to `rustc_target` in the same manner that the +# `rustc` driver script communicate this. +jemalloc = ["rustc_target/jemalloc"] + +# This is used to convince Cargo to separately cache builds of `rustc_codegen_llvm` +# when this option is enabled or not. That way we can build two, cache two +# artifacts, and have nice speedy rebuilds. +emscripten = ["rustc_llvm/emscripten"] diff --git a/src/librustc_codegen_llvm/README.md b/src/librustc_codegen_llvm/README.md new file mode 100644 index 00000000000..8d1c9a52b24 --- /dev/null +++ b/src/librustc_codegen_llvm/README.md @@ -0,0 +1,7 @@ +The `codegen` crate contains the code to convert from MIR into LLVM IR, +and then from LLVM IR into machine code. In general it contains code +that runs towards the end of the compilation process. + +For more information about how codegen works, see the [rustc guide]. + +[rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/codegen.html diff --git a/src/librustc_codegen_llvm/abi.rs b/src/librustc_codegen_llvm/abi.rs new file mode 100644 index 00000000000..25c598c532c --- /dev/null +++ b/src/librustc_codegen_llvm/abi.rs @@ -0,0 +1,696 @@ +// Copyright 2012-2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef, AttributePlace}; +use base; +use builder::{Builder, MemFlags}; +use common::{ty_fn_sig, C_usize}; +use context::CodegenCx; +use mir::place::PlaceRef; +use mir::operand::OperandValue; +use type_::Type; +use type_of::{LayoutLlvmExt, PointerKind}; + +use rustc_target::abi::{LayoutOf, Size, TyLayout}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout; + +use libc::c_uint; + +pub use rustc_target::spec::abi::Abi; +pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; +pub use rustc_target::abi::call::*; + +macro_rules! for_each_kind { + ($flags: ident, $f: ident, $($kind: ident),+) => ({ + $(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+ + }) +} + +trait ArgAttributeExt { + fn for_each_kind(&self, f: F) where F: FnMut(llvm::Attribute); +} + +impl ArgAttributeExt for ArgAttribute { + fn for_each_kind(&self, mut f: F) where F: FnMut(llvm::Attribute) { + for_each_kind!(self, f, + ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) + } +} + +pub trait ArgAttributesExt { + fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef); + fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef); +} + +impl ArgAttributesExt for ArgAttributes { + fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { + let mut regular = self.regular; + unsafe { + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableAttr(llfn, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentAttr(llfn, + idx.as_uint(), + align.abi() as u32); + } + regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); + } + } + + fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef) { + let mut regular = self.regular; + unsafe { + let deref = self.pointee_size.bytes(); + if deref != 0 { + if regular.contains(ArgAttribute::NonNull) { + llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, + idx.as_uint(), + deref); + } else { + llvm::LLVMRustAddDereferenceableOrNullCallSiteAttr(callsite, + idx.as_uint(), + deref); + } + regular -= ArgAttribute::NonNull; + } + if let Some(align) = self.pointee_align { + llvm::LLVMRustAddAlignmentCallSiteAttr(callsite, + idx.as_uint(), + align.abi() as u32); + } + regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); + } + } +} + +pub trait LlvmType { + fn llvm_type(&self, cx: &CodegenCx) -> Type; +} + +impl LlvmType for Reg { + fn llvm_type(&self, cx: &CodegenCx) -> Type { + match self.kind { + RegKind::Integer => Type::ix(cx, self.size.bits()), + RegKind::Float => { + match self.size.bits() { + 32 => Type::f32(cx), + 64 => Type::f64(cx), + _ => bug!("unsupported float: {:?}", self) + } + } + RegKind::Vector => { + Type::vector(&Type::i8(cx), self.size.bytes()) + } + } + } +} + +impl LlvmType for CastTarget { + fn llvm_type(&self, cx: &CodegenCx) -> Type { + let rest_ll_unit = self.rest.unit.llvm_type(cx); + let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); + let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); + + if self.prefix.iter().all(|x| x.is_none()) { + // Simplify to a single unit when there is no prefix and size <= unit size + if self.rest.total <= self.rest.unit.size { + return rest_ll_unit; + } + + // Simplify to array when all chunks are the same size and type + if rem_bytes == 0 { + return Type::array(&rest_ll_unit, rest_count); + } + } + + // Create list of fields in the main structure + let mut args: Vec<_> = + self.prefix.iter().flat_map(|option_kind| option_kind.map( + |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) + .chain((0..rest_count).map(|_| rest_ll_unit)) + .collect(); + + // Append final integer + if rem_bytes != 0 { + // Only integers can be really split further. + assert_eq!(self.rest.unit.kind, RegKind::Integer); + args.push(Type::ix(cx, rem_bytes * 8)); + } + + Type::struct_(cx, &args, false) + } +} + +pub trait ArgTypeExt<'a, 'tcx> { + fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>); + fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>); +} + +impl<'a, 'tcx> ArgTypeExt<'a, 'tcx> for ArgType<'tcx, Ty<'tcx>> { + /// Get the LLVM type for a place of the original Rust type of + /// this argument/return, i.e. the result of `type_of::type_of`. + fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + self.layout.llvm_type(cx) + } + + /// Store a direct/indirect value described by this ArgType into a + /// place for the original Rust type of this argument/return. + /// Can be used for both storing formal arguments into Rust variables + /// or results of call/invoke instructions into their destinations. + fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>) { + if self.is_ignore() { + return; + } + let cx = bx.cx; + if self.is_indirect() { + OperandValue::Ref(val, self.layout.align).store(bx, dst) + } else if let PassMode::Cast(cast) = self.mode { + // FIXME(eddyb): Figure out when the simpler Store is safe, clang + // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. + let can_store_through_cast_ptr = false; + if can_store_through_cast_ptr { + let cast_dst = bx.pointercast(dst.llval, cast.llvm_type(cx).ptr_to()); + bx.store(val, cast_dst, self.layout.align); + } else { + // The actual return type is a struct, but the ABI + // adaptation code has cast it into some scalar type. The + // code that follows is the only reliable way I have + // found to do a transform like i64 -> {i32,i32}. + // Basically we dump the data onto the stack then memcpy it. + // + // Other approaches I tried: + // - Casting rust ret pointer to the foreign type and using Store + // is (a) unsafe if size of foreign type > size of rust type and + // (b) runs afoul of strict aliasing rules, yielding invalid + // assembly under -O (specifically, the store gets removed). + // - Truncating foreign type to correct integral type and then + // bitcasting to the struct type yields invalid cast errors. + + // We instead thus allocate some scratch space... + let scratch_size = cast.size(cx); + let scratch_align = cast.align(cx); + let llscratch = bx.alloca(cast.llvm_type(cx), "abi_cast", scratch_align); + bx.lifetime_start(llscratch, scratch_size); + + // ...where we first store the value... + bx.store(val, llscratch, scratch_align); + + // ...and then memcpy it to the intended destination. + base::call_memcpy(bx, + bx.pointercast(dst.llval, Type::i8p(cx)), + bx.pointercast(llscratch, Type::i8p(cx)), + C_usize(cx, self.layout.size.bytes()), + self.layout.align.min(scratch_align), + MemFlags::empty()); + + bx.lifetime_end(llscratch, scratch_size); + } + } else { + OperandValue::Immediate(val).store(bx, dst); + } + } + + fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>) { + let mut next = || { + let val = llvm::get_param(bx.llfn(), *idx as c_uint); + *idx += 1; + val + }; + match self.mode { + PassMode::Ignore => {}, + PassMode::Pair(..) => { + OperandValue::Pair(next(), next()).store(bx, dst); + } + PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { + self.store(bx, next(), dst); + } + } + } +} + +pub trait FnTypeExt<'a, 'tcx> { + fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) + -> Self; + fn new(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn new_vtable(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn unadjusted(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self; + fn adjust_for_abi(&mut self, + cx: &CodegenCx<'a, 'tcx>, + abi: Abi); + fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn llvm_cconv(&self) -> llvm::CallConv; + fn apply_attrs_llfn(&self, llfn: ValueRef); + fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef); +} + +impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> { + fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) + -> Self { + let fn_ty = instance.ty(cx.tcx); + let sig = ty_fn_sig(cx, fn_ty); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + FnType::new(cx, sig, &[]) + } + + fn new(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); + fn_ty.adjust_for_abi(cx, sig.abi); + fn_ty + } + + fn new_vtable(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); + // Don't pass the vtable, it's not an argument of the virtual fn. + { + let self_arg = &mut fn_ty.args[0]; + match self_arg.mode { + PassMode::Pair(data_ptr, _) => { + self_arg.mode = PassMode::Direct(data_ptr); + } + _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) + } + + let pointee = self_arg.layout.ty.builtin_deref(true) + .unwrap_or_else(|| { + bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) + }).ty; + let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee); + self_arg.layout = cx.layout_of(fat_ptr_ty).field(cx, 0); + } + fn_ty.adjust_for_abi(cx, sig.abi); + fn_ty + } + + fn unadjusted(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>, + extra_args: &[Ty<'tcx>]) -> Self { + debug!("FnType::unadjusted({:?}, {:?})", sig, extra_args); + + use self::Abi::*; + let conv = match cx.sess().target.target.adjust_abi(sig.abi) { + RustIntrinsic | PlatformIntrinsic | + Rust | RustCall => Conv::C, + + // It's the ABI's job to select this, not us. + System => bug!("system abi should be selected elsewhere"), + + Stdcall => Conv::X86Stdcall, + Fastcall => Conv::X86Fastcall, + Vectorcall => Conv::X86VectorCall, + Thiscall => Conv::X86ThisCall, + C => Conv::C, + Unadjusted => Conv::C, + Win64 => Conv::X86_64Win64, + SysV64 => Conv::X86_64SysV, + Aapcs => Conv::ArmAapcs, + PtxKernel => Conv::PtxKernel, + Msp430Interrupt => Conv::Msp430Intr, + X86Interrupt => Conv::X86Intr, + + // These API constants ought to be more specific... + Cdecl => Conv::C, + }; + + let mut inputs = sig.inputs(); + let extra_args = if sig.abi == RustCall { + assert!(!sig.variadic && extra_args.is_empty()); + + match sig.inputs().last().unwrap().sty { + ty::TyTuple(ref tupled_arguments) => { + inputs = &sig.inputs()[0..sig.inputs().len() - 1]; + tupled_arguments + } + _ => { + bug!("argument to function with \"rust-call\" ABI \ + is not a tuple"); + } + } + } else { + assert!(sig.variadic || extra_args.is_empty()); + extra_args + }; + + let target = &cx.sess().target.target; + let win_x64_gnu = target.target_os == "windows" + && target.arch == "x86_64" + && target.target_env == "gnu"; + let linux_s390x = target.target_os == "linux" + && target.arch == "s390x" + && target.target_env == "gnu"; + let rust_abi = match sig.abi { + RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, + _ => false + }; + + // Handle safe Rust thin and fat pointers. + let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, + scalar: &layout::Scalar, + layout: TyLayout<'tcx, Ty<'tcx>>, + offset: Size, + is_return: bool| { + // Booleans are always an i1 that needs to be zero-extended. + if scalar.is_bool() { + attrs.set(ArgAttribute::ZExt); + return; + } + + // Only pointer types handled below. + if scalar.value != layout::Pointer { + return; + } + + if scalar.valid_range.start() < scalar.valid_range.end() { + if *scalar.valid_range.start() > 0 { + attrs.set(ArgAttribute::NonNull); + } + } + + if let Some(pointee) = layout.pointee_info_at(cx, offset) { + if let Some(kind) = pointee.safe { + attrs.pointee_size = pointee.size; + attrs.pointee_align = Some(pointee.align); + + // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions + // with align attributes, and those calls later block optimizations. + if !is_return && !cx.tcx.sess.opts.debugging_opts.arg_align_attributes { + attrs.pointee_align = None; + } + + // `Box` pointer parameters never alias because ownership is transferred + // `&mut` pointer parameters never alias other parameters, + // or mutable global data + // + // `&T` where `T` contains no `UnsafeCell` is immutable, + // and can be marked as both `readonly` and `noalias`, as + // LLVM's definition of `noalias` is based solely on memory + // dependencies rather than pointer equality + let no_alias = match kind { + PointerKind::Shared => false, + PointerKind::UniqueOwned => true, + PointerKind::Frozen | + PointerKind::UniqueBorrowed => !is_return + }; + if no_alias { + attrs.set(ArgAttribute::NoAlias); + } + + if kind == PointerKind::Frozen && !is_return { + attrs.set(ArgAttribute::ReadOnly); + } + } + } + }; + + let arg_of = |ty: Ty<'tcx>, is_return: bool| { + let mut arg = ArgType::new(cx.layout_of(ty)); + if arg.layout.is_zst() { + // For some forsaken reason, x86_64-pc-windows-gnu + // doesn't ignore zero-sized struct arguments. + // The same is true for s390x-unknown-linux-gnu. + if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { + arg.mode = PassMode::Ignore; + } + } + + // FIXME(eddyb) other ABIs don't have logic for scalar pairs. + if !is_return && rust_abi { + if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { + let mut a_attrs = ArgAttributes::new(); + let mut b_attrs = ArgAttributes::new(); + adjust_for_rust_scalar(&mut a_attrs, + a, + arg.layout, + Size::from_bytes(0), + false); + adjust_for_rust_scalar(&mut b_attrs, + b, + arg.layout, + a.value.size(cx).abi_align(b.value.align(cx)), + false); + arg.mode = PassMode::Pair(a_attrs, b_attrs); + return arg; + } + } + + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if let PassMode::Direct(ref mut attrs) = arg.mode { + adjust_for_rust_scalar(attrs, + scalar, + arg.layout, + Size::from_bytes(0), + is_return); + } + } + + arg + }; + + FnType { + ret: arg_of(sig.output(), true), + args: inputs.iter().chain(extra_args.iter()).map(|ty| { + arg_of(ty, false) + }).collect(), + variadic: sig.variadic, + conv, + } + } + + fn adjust_for_abi(&mut self, + cx: &CodegenCx<'a, 'tcx>, + abi: Abi) { + if abi == Abi::Unadjusted { return } + + if abi == Abi::Rust || abi == Abi::RustCall || + abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { + let fixup = |arg: &mut ArgType<'tcx, Ty<'tcx>>| { + if arg.is_ignore() { return; } + + match arg.layout.abi { + layout::Abi::Aggregate { .. } => {} + + // This is a fun case! The gist of what this is doing is + // that we want callers and callees to always agree on the + // ABI of how they pass SIMD arguments. If we were to *not* + // make these arguments indirect then they'd be immediates + // in LLVM, which means that they'd used whatever the + // appropriate ABI is for the callee and the caller. That + // means, for example, if the caller doesn't have AVX + // enabled but the callee does, then passing an AVX argument + // across this boundary would cause corrupt data to show up. + // + // This problem is fixed by unconditionally passing SIMD + // arguments through memory between callers and callees + // which should get them all to agree on ABI regardless of + // target feature sets. Some more information about this + // issue can be found in #44367. + // + // Note that the platform intrinsic ABI is exempt here as + // that's how we connect up to LLVM and it's unstable + // anyway, we control all calls to it in libstd. + layout::Abi::Vector { .. } if abi != Abi::PlatformIntrinsic => { + arg.make_indirect(); + return + } + + _ => return + } + + let size = arg.layout.size; + if size > layout::Pointer.size(cx) { + arg.make_indirect(); + } else { + // We want to pass small aggregates as immediates, but using + // a LLVM aggregate type for this leads to bad optimizations, + // so we pick an appropriately sized integer type instead. + arg.cast_to(Reg { + kind: RegKind::Integer, + size + }); + } + }; + fixup(&mut self.ret); + for arg in &mut self.args { + fixup(arg); + } + if let PassMode::Indirect(ref mut attrs) = self.ret.mode { + attrs.set(ArgAttribute::StructRet); + } + return; + } + + if let Err(msg) = self.adjust_for_cabi(cx, abi) { + cx.sess().fatal(&msg); + } + } + + fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + let mut llargument_tys = Vec::new(); + + let llreturn_ty = match self.ret.mode { + PassMode::Ignore => Type::void(cx), + PassMode::Direct(_) | PassMode::Pair(..) => { + self.ret.layout.immediate_llvm_type(cx) + } + PassMode::Cast(cast) => cast.llvm_type(cx), + PassMode::Indirect(_) => { + llargument_tys.push(self.ret.memory_ty(cx).ptr_to()); + Type::void(cx) + } + }; + + for arg in &self.args { + // add padding + if let Some(ty) = arg.pad { + llargument_tys.push(ty.llvm_type(cx)); + } + + let llarg_ty = match arg.mode { + PassMode::Ignore => continue, + PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), + PassMode::Pair(..) => { + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0)); + llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1)); + continue; + } + PassMode::Cast(cast) => cast.llvm_type(cx), + PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(), + }; + llargument_tys.push(llarg_ty); + } + + if self.variadic { + Type::variadic_func(&llargument_tys, &llreturn_ty) + } else { + Type::func(&llargument_tys, &llreturn_ty) + } + } + + fn llvm_cconv(&self) -> llvm::CallConv { + match self.conv { + Conv::C => llvm::CCallConv, + Conv::ArmAapcs => llvm::ArmAapcsCallConv, + Conv::Msp430Intr => llvm::Msp430Intr, + Conv::PtxKernel => llvm::PtxKernel, + Conv::X86Fastcall => llvm::X86FastcallCallConv, + Conv::X86Intr => llvm::X86_Intr, + Conv::X86Stdcall => llvm::X86StdcallCallConv, + Conv::X86ThisCall => llvm::X86_ThisCall, + Conv::X86VectorCall => llvm::X86_VectorCall, + Conv::X86_64SysV => llvm::X86_64_SysV, + Conv::X86_64Win64 => llvm::X86_64_Win64, + } + } + + fn apply_attrs_llfn(&self, llfn: ValueRef) { + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), + } + } + } + + fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) { + let mut i = 0; + let mut apply = |attrs: &ArgAttributes| { + attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); + i += 1; + }; + match self.ret.mode { + PassMode::Direct(ref attrs) => { + attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); + } + PassMode::Indirect(ref attrs) => apply(attrs), + _ => {} + } + if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi { + // If the value is a boolean, the range is 0..2 and that ultimately + // become 0..0 when the type becomes i1, which would be rejected + // by the LLVM verifier. + match scalar.value { + layout::Int(..) if !scalar.is_bool() => { + let range = scalar.valid_range_exclusive(bx.cx); + if range.start != range.end { + // FIXME(nox): This causes very weird type errors about + // SHL operators in constants in stage 2 with LLVM 3.9. + if unsafe { llvm::LLVMRustVersionMajor() >= 4 } { + bx.range_metadata(callsite, range); + } + } + } + _ => {} + } + } + for arg in &self.args { + if arg.pad.is_some() { + apply(&ArgAttributes::new()); + } + match arg.mode { + PassMode::Ignore => {} + PassMode::Direct(ref attrs) | + PassMode::Indirect(ref attrs) => apply(attrs), + PassMode::Pair(ref a, ref b) => { + apply(a); + apply(b); + } + PassMode::Cast(_) => apply(&ArgAttributes::new()), + } + } + + let cconv = self.llvm_cconv(); + if cconv != llvm::CCallConv { + llvm::SetInstructionCallConv(callsite, cconv); + } + } +} diff --git a/src/librustc_codegen_llvm/allocator.rs b/src/librustc_codegen_llvm/allocator.rs new file mode 100644 index 00000000000..eeb02e948f1 --- /dev/null +++ b/src/librustc_codegen_llvm/allocator.rs @@ -0,0 +1,103 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::ffi::CString; +use std::ptr; + +use attributes; +use libc::c_uint; +use rustc::middle::allocator::AllocatorKind; +use rustc::ty::TyCtxt; +use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; + +use ModuleLlvm; +use llvm::{self, False, True}; + +pub(crate) unsafe fn codegen(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { + let llcx = mods.llcx; + let llmod = mods.llmod; + let usize = match &tcx.sess.target.target.target_pointer_width[..] { + "16" => llvm::LLVMInt16TypeInContext(llcx), + "32" => llvm::LLVMInt32TypeInContext(llcx), + "64" => llvm::LLVMInt64TypeInContext(llcx), + tws => bug!("Unsupported target word size for int: {}", tws), + }; + let i8 = llvm::LLVMInt8TypeInContext(llcx); + let i8p = llvm::LLVMPointerType(i8, 0); + let void = llvm::LLVMVoidTypeInContext(llcx); + + for method in ALLOCATOR_METHODS { + let mut args = Vec::new(); + for ty in method.inputs.iter() { + match *ty { + AllocatorTy::Layout => { + args.push(usize); // size + args.push(usize); // align + } + AllocatorTy::Ptr => args.push(i8p), + AllocatorTy::Usize => args.push(usize), + + AllocatorTy::ResultPtr | + AllocatorTy::Unit => panic!("invalid allocator arg"), + } + } + let output = match method.output { + AllocatorTy::ResultPtr => Some(i8p), + AllocatorTy::Unit => None, + + AllocatorTy::Layout | + AllocatorTy::Usize | + AllocatorTy::Ptr => panic!("invalid allocator output"), + }; + let ty = llvm::LLVMFunctionType(output.unwrap_or(void), + args.as_ptr(), + args.len() as c_uint, + False); + let name = CString::new(format!("__rust_{}", method.name)).unwrap(); + let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, + name.as_ptr(), + ty); + + if tcx.sess.target.target.options.default_hidden_visibility { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + if tcx.sess.target.target.options.requires_uwtable { + attributes::emit_uwtable(llfn, true); + } + + let callee = CString::new(kind.fn_name(method.name)).unwrap(); + let callee = llvm::LLVMRustGetOrInsertFunction(llmod, + callee.as_ptr(), + ty); + + let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, + llfn, + "entry\0".as_ptr() as *const _); + + let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); + llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); + let args = args.iter().enumerate().map(|(i, _)| { + llvm::LLVMGetParam(llfn, i as c_uint) + }).collect::>(); + let ret = llvm::LLVMRustBuildCall(llbuilder, + callee, + args.as_ptr(), + args.len() as c_uint, + ptr::null_mut(), + "\0".as_ptr() as *const _); + llvm::LLVMSetTailCall(ret, True); + if output.is_some() { + llvm::LLVMBuildRet(llbuilder, ret); + } else { + llvm::LLVMBuildRetVoid(llbuilder); + } + llvm::LLVMDisposeBuilder(llbuilder); + } +} diff --git a/src/librustc_codegen_llvm/asm.rs b/src/librustc_codegen_llvm/asm.rs new file mode 100644 index 00000000000..82f068106ff --- /dev/null +++ b/src/librustc_codegen_llvm/asm.rs @@ -0,0 +1,127 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use common::*; +use type_::Type; +use type_of::LayoutLlvmExt; +use builder::Builder; + +use rustc::hir; + +use mir::place::PlaceRef; +use mir::operand::OperandValue; + +use std::ffi::CString; +use syntax::ast::AsmDialect; +use libc::{c_uint, c_char}; + +// Take an inline assembly expression and splat it out via LLVM +pub fn codegen_inline_asm<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + ia: &hir::InlineAsm, + outputs: Vec>, + mut inputs: Vec +) { + let mut ext_constraints = vec![]; + let mut output_types = vec![]; + + // Prepare the output operands + let mut indirect_outputs = vec![]; + for (i, (out, place)) in ia.outputs.iter().zip(&outputs).enumerate() { + if out.is_rw { + inputs.push(place.load(bx).immediate()); + ext_constraints.push(i.to_string()); + } + if out.is_indirect { + indirect_outputs.push(place.load(bx).immediate()); + } else { + output_types.push(place.layout.llvm_type(bx.cx)); + } + } + if !indirect_outputs.is_empty() { + indirect_outputs.extend_from_slice(&inputs); + inputs = indirect_outputs; + } + + let clobbers = ia.clobbers.iter() + .map(|s| format!("~{{{}}}", &s)); + + // Default per-arch clobbers + // Basically what clang does + let arch_clobbers = match &bx.sess().target.target.arch[..] { + "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], + "mips" | "mips64" => vec!["~{$1}"], + _ => Vec::new() + }; + + let all_constraints = + ia.outputs.iter().map(|out| out.constraint.to_string()) + .chain(ia.inputs.iter().map(|s| s.to_string())) + .chain(ext_constraints) + .chain(clobbers) + .chain(arch_clobbers.iter().map(|s| s.to_string())) + .collect::>().join(","); + + debug!("Asm Constraints: {}", &all_constraints); + + // Depending on how many outputs we have, the return type is different + let num_outputs = output_types.len(); + let output_type = match num_outputs { + 0 => Type::void(bx.cx), + 1 => output_types[0], + _ => Type::struct_(bx.cx, &output_types, false) + }; + + let dialect = match ia.dialect { + AsmDialect::Att => llvm::AsmDialect::Att, + AsmDialect::Intel => llvm::AsmDialect::Intel, + }; + + let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); + let constraint_cstr = CString::new(all_constraints).unwrap(); + let r = bx.inline_asm_call( + asm.as_ptr(), + constraint_cstr.as_ptr(), + &inputs, + output_type, + ia.volatile, + ia.alignstack, + dialect + ); + + // Again, based on how many outputs we have + let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); + for (i, (_, &place)) in outputs.enumerate() { + let v = if num_outputs == 1 { r } else { bx.extract_value(r, i as u64) }; + OperandValue::Immediate(v).store(bx, place); + } + + // Store mark in a metadata node so we can map LLVM errors + // back to source locations. See #17552. + unsafe { + let key = "srcloc"; + let kind = llvm::LLVMGetMDKindIDInContext(bx.cx.llcx, + key.as_ptr() as *const c_char, key.len() as c_uint); + + let val: llvm::ValueRef = C_i32(bx.cx, ia.ctxt.outer().as_u32() as i32); + + llvm::LLVMSetMetadata(r, kind, + llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1)); + } +} + +pub fn codegen_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ga: &hir::GlobalAsm) { + let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap(); + unsafe { + llvm::LLVMRustAppendModuleInlineAsm(cx.llmod, asm.as_ptr()); + } +} diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs new file mode 100644 index 00000000000..b64e102ba78 --- /dev/null +++ b/src/librustc_codegen_llvm/attributes.rs @@ -0,0 +1,259 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! Set and unset common attributes on LLVM values. + +use std::ffi::{CStr, CString}; + +use rustc::hir::{self, CodegenFnAttrFlags}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::itemlikevisit::ItemLikeVisitor; +use rustc::session::Session; +use rustc::session::config::Sanitizer; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc_data_structures::sync::Lrc; +use rustc_data_structures::fx::FxHashMap; + +use llvm::{self, Attribute, ValueRef}; +use llvm::AttributePlace::Function; +use llvm_util; +pub use syntax::attr::{self, InlineAttr}; +use context::CodegenCx; + +/// Mark LLVM function to use provided inline heuristic. +#[inline] +pub fn inline(val: ValueRef, inline: InlineAttr) { + use self::InlineAttr::*; + match inline { + Hint => Attribute::InlineHint.apply_llfn(Function, val), + Always => Attribute::AlwaysInline.apply_llfn(Function, val), + Never => Attribute::NoInline.apply_llfn(Function, val), + None => { + Attribute::InlineHint.unapply_llfn(Function, val); + Attribute::AlwaysInline.unapply_llfn(Function, val); + Attribute::NoInline.unapply_llfn(Function, val); + }, + }; +} + +/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. +#[inline] +pub fn emit_uwtable(val: ValueRef, emit: bool) { + Attribute::UWTable.toggle_llfn(Function, val, emit); +} + +/// Tell LLVM whether the function can or cannot unwind. +#[inline] +pub fn unwind(val: ValueRef, can_unwind: bool) { + Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); +} + +/// Tell LLVM whether it should optimize function for size. +#[inline] +#[allow(dead_code)] // possibly useful function +pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { + Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize); +} + +/// Tell LLVM if this function should be 'naked', i.e. skip the epilogue and prologue. +#[inline] +pub fn naked(val: ValueRef, is_naked: bool) { + Attribute::Naked.toggle_llfn(Function, val, is_naked); +} + +pub fn set_frame_pointer_elimination(cx: &CodegenCx, llfn: ValueRef) { + if cx.sess().must_not_eliminate_frame_pointers() { + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("no-frame-pointer-elim\0"), cstr("true\0")); + } +} + +pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) { + // Only use stack probes if the target specification indicates that we + // should be using stack probes + if !cx.sess().target.target.options.stack_probes { + return + } + + // Currently stack probes seem somewhat incompatible with the address + // sanitizer. With asan we're already protected from stack overflow anyway + // so we don't really need stack probes regardless. + match cx.sess().opts.debugging_opts.sanitizer { + Some(Sanitizer::Address) => return, + _ => {} + } + + // probestack doesn't play nice either with pgo-gen. + if cx.sess().opts.debugging_opts.pgo_gen.is_some() { + return; + } + + // Flag our internal `__rust_probestack` function as the stack probe symbol. + // This is defined in the `compiler-builtins` crate for each architecture. + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("probe-stack\0"), cstr("__rust_probestack\0")); +} + +pub fn llvm_target_features(sess: &Session) -> impl Iterator { + const RUSTC_SPECIFIC_FEATURES: &[&str] = &[ + "crt-static", + ]; + + let cmdline = sess.opts.cg.target_feature.split(',') + .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); + sess.target.target.options.features.split(',') + .chain(cmdline) + .filter(|l| !l.is_empty()) +} + +/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) +/// attributes. +pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { + let codegen_fn_attrs = cx.tcx.codegen_fn_attrs(id); + + inline(llfn, codegen_fn_attrs.inline); + + set_frame_pointer_elimination(cx, llfn); + set_probestack(cx, llfn); + + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { + Attribute::Cold.apply_llfn(Function, llfn); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::NAKED) { + naked(llfn, true); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::ALLOCATOR) { + Attribute::NoAlias.apply_llfn( + llvm::AttributePlace::ReturnValue, llfn); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { + unwind(llfn, true); + } + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + unwind(llfn, false); + } + + let features = llvm_target_features(cx.tcx.sess) + .map(|s| s.to_string()) + .chain( + codegen_fn_attrs.target_features + .iter() + .map(|f| { + let feature = &*f.as_str(); + format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) + }) + ) + .collect::>() + .join(","); + + if !features.is_empty() { + let val = CString::new(features).unwrap(); + llvm::AddFunctionAttrStringValue( + llfn, llvm::AttributePlace::Function, + cstr("target-features\0"), &val); + } + + // Note that currently the `wasm-import-module` doesn't do anything, but + // eventually LLVM 7 should read this and ferry the appropriate import + // module to the output file. + if cx.tcx.sess.target.target.arch == "wasm32" { + if let Some(module) = wasm_import_module(cx.tcx, id) { + llvm::AddFunctionAttrStringValue( + llfn, + llvm::AttributePlace::Function, + cstr("wasm-import-module\0"), + &module, + ); + } + } +} + +fn cstr(s: &'static str) -> &CStr { + CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") +} + +pub fn provide(providers: &mut Providers) { + providers.target_features_whitelist = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + if tcx.sess.opts.actually_rustdoc { + // rustdoc needs to be able to document functions that use all the features, so + // whitelist them all + Lrc::new(llvm_util::all_known_features() + .map(|(a, b)| (a.to_string(), b.map(|s| s.to_string()))) + .collect()) + } else { + Lrc::new(llvm_util::target_feature_whitelist(tcx.sess) + .iter() + .map(|&(a, b)| (a.to_string(), b.map(|s| s.to_string()))) + .collect()) + } + }; + + providers.wasm_custom_sections = |tcx, cnum| { + assert_eq!(cnum, LOCAL_CRATE); + let mut finder = WasmSectionFinder { tcx, list: Vec::new() }; + tcx.hir.krate().visit_all_item_likes(&mut finder); + Lrc::new(finder.list) + }; + + provide_extern(providers); +} + +struct WasmSectionFinder<'a, 'tcx: 'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, + list: Vec, +} + +impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> { + fn visit_item(&mut self, i: &'tcx hir::Item) { + match i.node { + hir::ItemConst(..) => {} + _ => return, + } + if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) { + self.list.push(self.tcx.hir.local_def_id(i.id)); + } + } + + fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} + + fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} +} + +pub fn provide_extern(providers: &mut Providers) { + providers.wasm_import_module_map = |tcx, cnum| { + let mut ret = FxHashMap(); + for lib in tcx.foreign_modules(cnum).iter() { + let attrs = tcx.get_attrs(lib.def_id); + let mut module = None; + for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) { + module = attr.value_str(); + } + let module = match module { + Some(s) => s, + None => continue, + }; + for id in lib.foreign_items.iter() { + assert_eq!(id.krate, cnum); + ret.insert(*id, module.to_string()); + } + } + + Lrc::new(ret) + } +} + +fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option { + tcx.wasm_import_module_map(id.krate) + .get(&id) + .map(|s| CString::new(&s[..]).unwrap()) +} diff --git a/src/librustc_codegen_llvm/back/archive.rs b/src/librustc_codegen_llvm/back/archive.rs new file mode 100644 index 00000000000..609629bffb9 --- /dev/null +++ b/src/librustc_codegen_llvm/back/archive.rs @@ -0,0 +1,325 @@ +// Copyright 2013-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A helper class for dealing with static archives + +use std::ffi::{CString, CStr}; +use std::io; +use std::mem; +use std::path::{Path, PathBuf}; +use std::ptr; +use std::str; + +use back::bytecode::RLIB_BYTECODE_EXTENSION; +use libc; +use llvm::archive_ro::{ArchiveRO, Child}; +use llvm::{self, ArchiveKind}; +use metadata::METADATA_FILENAME; +use rustc::session::Session; + +pub struct ArchiveConfig<'a> { + pub sess: &'a Session, + pub dst: PathBuf, + pub src: Option, + pub lib_search_paths: Vec, +} + +/// Helper for adding many files to an archive. +#[must_use = "must call build() to finish building the archive"] +pub struct ArchiveBuilder<'a> { + config: ArchiveConfig<'a>, + removals: Vec, + additions: Vec, + should_update_symbols: bool, + src_archive: Option>, +} + +enum Addition { + File { + path: PathBuf, + name_in_archive: String, + }, + Archive { + archive: ArchiveRO, + skip: Box bool>, + }, +} + +pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) + -> PathBuf { + // On Windows, static libraries sometimes show up as libfoo.a and other + // times show up as foo.lib + let oslibname = format!("{}{}{}", + sess.target.target.options.staticlib_prefix, + name, + sess.target.target.options.staticlib_suffix); + let unixlibname = format!("lib{}.a", name); + + for path in search_paths { + debug!("looking for {} inside {:?}", name, path); + let test = path.join(&oslibname); + if test.exists() { return test } + if oslibname != unixlibname { + let test = path.join(&unixlibname); + if test.exists() { return test } + } + } + sess.fatal(&format!("could not find native static library `{}`, \ + perhaps an -L flag is missing?", name)); +} + +fn is_relevant_child(c: &Child) -> bool { + match c.name() { + Some(name) => !name.contains("SYMDEF"), + None => false, + } +} + +impl<'a> ArchiveBuilder<'a> { + /// Create a new static archive, ready for modifying the archive specified + /// by `config`. + pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { + ArchiveBuilder { + config, + removals: Vec::new(), + additions: Vec::new(), + should_update_symbols: false, + src_archive: None, + } + } + + /// Removes a file from this archive + pub fn remove_file(&mut self, file: &str) { + self.removals.push(file.to_string()); + } + + /// Lists all files in an archive + pub fn src_files(&mut self) -> Vec { + if self.src_archive().is_none() { + return Vec::new() + } + let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); + let ret = archive.iter() + .filter_map(|child| child.ok()) + .filter(is_relevant_child) + .filter_map(|child| child.name()) + .filter(|name| !self.removals.iter().any(|x| x == name)) + .map(|name| name.to_string()) + .collect(); + return ret; + } + + fn src_archive(&mut self) -> Option<&ArchiveRO> { + if let Some(ref a) = self.src_archive { + return a.as_ref() + } + let src = self.config.src.as_ref()?; + self.src_archive = Some(ArchiveRO::open(src).ok()); + self.src_archive.as_ref().unwrap().as_ref() + } + + /// Adds all of the contents of a native library to this archive. This will + /// search in the relevant locations for a library named `name`. + pub fn add_native_library(&mut self, name: &str) { + let location = find_library(name, &self.config.lib_search_paths, + self.config.sess); + self.add_archive(&location, |_| false).unwrap_or_else(|e| { + self.config.sess.fatal(&format!("failed to add native library {}: {}", + location.to_string_lossy(), e)); + }); + } + + /// Adds all of the contents of the rlib at the specified path to this + /// archive. + /// + /// This ignores adding the bytecode from the rlib, and if LTO is enabled + /// then the object file also isn't added. + pub fn add_rlib(&mut self, + rlib: &Path, + name: &str, + lto: bool, + skip_objects: bool) -> io::Result<()> { + // Ignoring obj file starting with the crate name + // as simple comparison is not enough - there + // might be also an extra name suffix + let obj_start = format!("{}", name); + + self.add_archive(rlib, move |fname: &str| { + // Ignore bytecode/metadata files, no matter the name. + if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { + return true + } + + // Don't include Rust objects if LTO is enabled + if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { + return true + } + + // Otherwise if this is *not* a rust object and we're skipping + // objects then skip this file + if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { + return true + } + + // ok, don't skip this + return false + }) + } + + fn add_archive(&mut self, archive: &Path, skip: F) + -> io::Result<()> + where F: FnMut(&str) -> bool + 'static + { + let archive = match ArchiveRO::open(archive) { + Ok(ar) => ar, + Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), + }; + self.additions.push(Addition::Archive { + archive, + skip: Box::new(skip), + }); + Ok(()) + } + + /// Adds an arbitrary file to this archive + pub fn add_file(&mut self, file: &Path) { + let name = file.file_name().unwrap().to_str().unwrap(); + self.additions.push(Addition::File { + path: file.to_path_buf(), + name_in_archive: name.to_string(), + }); + } + + /// Indicate that the next call to `build` should update all symbols in + /// the archive (equivalent to running 'ar s' over it). + pub fn update_symbols(&mut self) { + self.should_update_symbols = true; + } + + /// Combine the provided files, rlibs, and native libraries into a single + /// `Archive`. + pub fn build(&mut self) { + let kind = match self.llvm_archive_kind() { + Ok(kind) => kind, + Err(kind) => { + self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", + kind)); + } + }; + + if let Err(e) = self.build_with_llvm(kind) { + self.config.sess.fatal(&format!("failed to build archive: {}", e)); + } + + } + + fn llvm_archive_kind(&self) -> Result { + let kind = &*self.config.sess.target.target.options.archive_format; + kind.parse().map_err(|_| kind) + } + + fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> { + let mut archives = Vec::new(); + let mut strings = Vec::new(); + let mut members = Vec::new(); + let removals = mem::replace(&mut self.removals, Vec::new()); + + unsafe { + if let Some(archive) = self.src_archive() { + for child in archive.iter() { + let child = child.map_err(string_to_io_error)?; + let child_name = match child.name() { + Some(s) => s, + None => continue, + }; + if removals.iter().any(|r| r == child_name) { + continue + } + + let name = CString::new(child_name)?; + members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(), + name.as_ptr(), + child.raw())); + strings.push(name); + } + } + for addition in mem::replace(&mut self.additions, Vec::new()) { + match addition { + Addition::File { path, name_in_archive } => { + let path = CString::new(path.to_str().unwrap())?; + let name = CString::new(name_in_archive)?; + members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(), + name.as_ptr(), + ptr::null_mut())); + strings.push(path); + strings.push(name); + } + Addition::Archive { archive, mut skip } => { + for child in archive.iter() { + let child = child.map_err(string_to_io_error)?; + if !is_relevant_child(&child) { + continue + } + let child_name = child.name().unwrap(); + if skip(child_name) { + continue + } + + // It appears that LLVM's archive writer is a little + // buggy if the name we pass down isn't just the + // filename component, so chop that off here and + // pass it in. + // + // See LLVM bug 25877 for more info. + let child_name = Path::new(child_name) + .file_name().unwrap() + .to_str().unwrap(); + let name = CString::new(child_name)?; + let m = llvm::LLVMRustArchiveMemberNew(ptr::null(), + name.as_ptr(), + child.raw()); + members.push(m); + strings.push(name); + } + archives.push(archive); + } + } + } + + let dst = self.config.dst.to_str().unwrap().as_bytes(); + let dst = CString::new(dst)?; + let r = llvm::LLVMRustWriteArchive(dst.as_ptr(), + members.len() as libc::size_t, + members.as_ptr(), + self.should_update_symbols, + kind); + let ret = if r.into_result().is_err() { + let err = llvm::LLVMRustGetLastError(); + let msg = if err.is_null() { + "failed to write archive".to_string() + } else { + String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) + .into_owned() + }; + Err(io::Error::new(io::ErrorKind::Other, msg)) + } else { + Ok(()) + }; + for member in members { + llvm::LLVMRustArchiveMemberFree(member); + } + return ret + } + } +} + +fn string_to_io_error(s: String) -> io::Error { + io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) +} diff --git a/src/librustc_codegen_llvm/back/bytecode.rs b/src/librustc_codegen_llvm/back/bytecode.rs new file mode 100644 index 00000000000..212d1aaf055 --- /dev/null +++ b/src/librustc_codegen_llvm/back/bytecode.rs @@ -0,0 +1,160 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Management of the encoding of LLVM bytecode into rlibs +//! +//! This module contains the management of encoding LLVM bytecode into rlibs, +//! primarily for the usage in LTO situations. Currently the compiler will +//! unconditionally encode LLVM-IR into rlibs regardless of what's happening +//! elsewhere, so we currently compress the bytecode via deflate to avoid taking +//! up too much space on disk. +//! +//! After compressing the bytecode we then have the rest of the format to +//! basically deal with various bugs in various archive implementations. The +//! format currently is: +//! +//! RLIB LLVM-BYTECODE OBJECT LAYOUT +//! Version 2 +//! Bytes Data +//! 0..10 "RUST_OBJECT" encoded in ASCII +//! 11..14 format version as little-endian u32 +//! 15..19 the length of the module identifier string +//! 20..n the module identifier string +//! n..n+8 size in bytes of deflate compressed LLVM bitcode as +//! little-endian u64 +//! n+9.. compressed LLVM bitcode +//! ? maybe a byte to make this whole thing even length + +use std::io::{Read, Write}; +use std::ptr; +use std::str; + +use flate2::Compression; +use flate2::read::DeflateDecoder; +use flate2::write::DeflateEncoder; + +// This is the "magic number" expected at the beginning of a LLVM bytecode +// object in an rlib. +pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; + +// The version number this compiler will write to bytecode objects in rlibs +pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; + +pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; + +pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { + let mut encoded = Vec::new(); + + // Start off with the magic string + encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); + + // Next up is the version + encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); + + // Next is the LLVM module identifier length + contents + let identifier_len = identifier.len(); + encoded.extend_from_slice(&[ + (identifier_len >> 0) as u8, + (identifier_len >> 8) as u8, + (identifier_len >> 16) as u8, + (identifier_len >> 24) as u8, + ]); + encoded.extend_from_slice(identifier.as_bytes()); + + // Next is the LLVM module deflate compressed, prefixed with its length. We + // don't know its length yet, so fill in 0s + let deflated_size_pos = encoded.len(); + encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); + + let before = encoded.len(); + DeflateEncoder::new(&mut encoded, Compression::fast()) + .write_all(bytecode) + .unwrap(); + let after = encoded.len(); + + // Fill in the length we reserved space for before + let bytecode_len = (after - before) as u64; + encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; + encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; + encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; + encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; + encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; + encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; + encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; + encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; + + // If the number of bytes written to the object so far is odd, add a + // padding byte to make it even. This works around a crash bug in LLDB + // (see issue #15950) + if encoded.len() % 2 == 1 { + encoded.push(0); + } + + return encoded +} + +pub struct DecodedBytecode<'a> { + identifier: &'a str, + encoded_bytecode: &'a [u8], +} + +impl<'a> DecodedBytecode<'a> { + pub fn new(data: &'a [u8]) -> Result, String> { + if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { + return Err(format!("magic bytecode prefix not found")) + } + let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; + if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { + return Err(format!("wrong version prefix found in bytecode")) + } + let data = &data[4..]; + if data.len() < 4 { + return Err(format!("bytecode corrupted")) + } + let identifier_len = unsafe { + u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize + }; + let data = &data[4..]; + if data.len() < identifier_len { + return Err(format!("bytecode corrupted")) + } + let identifier = match str::from_utf8(&data[..identifier_len]) { + Ok(s) => s, + Err(_) => return Err(format!("bytecode corrupted")) + }; + let data = &data[identifier_len..]; + if data.len() < 8 { + return Err(format!("bytecode corrupted")) + } + let bytecode_len = unsafe { + u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize + }; + let data = &data[8..]; + if data.len() < bytecode_len { + return Err(format!("bytecode corrupted")) + } + let encoded_bytecode = &data[..bytecode_len]; + + Ok(DecodedBytecode { + identifier, + encoded_bytecode, + }) + } + + pub fn bytecode(&self) -> Vec { + let mut data = Vec::new(); + DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); + return data + } + + pub fn identifier(&self) -> &'a str { + self.identifier + } +} diff --git a/src/librustc_codegen_llvm/back/command.rs b/src/librustc_codegen_llvm/back/command.rs new file mode 100644 index 00000000000..9ebbdd7c3c9 --- /dev/null +++ b/src/librustc_codegen_llvm/back/command.rs @@ -0,0 +1,175 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A thin wrapper around `Command` in the standard library which allows us to +//! read the arguments that are built up. + +use std::ffi::{OsStr, OsString}; +use std::fmt; +use std::io; +use std::mem; +use std::process::{self, Output}; + +use rustc_target::spec::LldFlavor; + +#[derive(Clone)] +pub struct Command { + program: Program, + args: Vec, + env: Vec<(OsString, OsString)>, +} + +#[derive(Clone)] +enum Program { + Normal(OsString), + CmdBatScript(OsString), + Lld(OsString, LldFlavor) +} + +impl Command { + pub fn new>(program: P) -> Command { + Command::_new(Program::Normal(program.as_ref().to_owned())) + } + + pub fn bat_script>(program: P) -> Command { + Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) + } + + pub fn lld>(program: P, flavor: LldFlavor) -> Command { + Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) + } + + fn _new(program: Program) -> Command { + Command { + program, + args: Vec::new(), + env: Vec::new(), + } + } + + pub fn arg>(&mut self, arg: P) -> &mut Command { + self._arg(arg.as_ref()); + self + } + + pub fn args(&mut self, args: I) -> &mut Command + where I: IntoIterator, + I::Item: AsRef, + { + for arg in args { + self._arg(arg.as_ref()); + } + self + } + + fn _arg(&mut self, arg: &OsStr) { + self.args.push(arg.to_owned()); + } + + pub fn env(&mut self, key: K, value: V) -> &mut Command + where K: AsRef, + V: AsRef + { + self._env(key.as_ref(), value.as_ref()); + self + } + + fn _env(&mut self, key: &OsStr, value: &OsStr) { + self.env.push((key.to_owned(), value.to_owned())); + } + + pub fn output(&mut self) -> io::Result { + self.command().output() + } + + pub fn command(&self) -> process::Command { + let mut ret = match self.program { + Program::Normal(ref p) => process::Command::new(p), + Program::CmdBatScript(ref p) => { + let mut c = process::Command::new("cmd"); + c.arg("/c").arg(p); + c + } + Program::Lld(ref p, flavor) => { + let mut c = process::Command::new(p); + c.arg("-flavor").arg(match flavor { + LldFlavor::Wasm => "wasm", + LldFlavor::Ld => "gnu", + LldFlavor::Link => "link", + LldFlavor::Ld64 => "darwin", + }); + c + } + }; + ret.args(&self.args); + ret.envs(self.env.clone()); + return ret + } + + // extensions + + pub fn get_args(&self) -> &[OsString] { + &self.args + } + + pub fn take_args(&mut self) -> Vec { + mem::replace(&mut self.args, Vec::new()) + } + + /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, + /// or `false` if we should attempt to spawn and see what the OS says. + pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { + // We mostly only care about Windows in this method, on Unix the limits + // can be gargantuan anyway so we're pretty unlikely to hit them + if cfg!(unix) { + return false + } + + // Right now LLD doesn't support the `@` syntax of passing an argument + // through files, so regardless of the platform we try to go to the OS + // on this one. + if let Program::Lld(..) = self.program { + return false + } + + // Ok so on Windows to spawn a process is 32,768 characters in its + // command line [1]. Unfortunately we don't actually have access to that + // as it's calculated just before spawning. Instead we perform a + // poor-man's guess as to how long our command line will be. We're + // assuming here that we don't have to escape every character... + // + // Turns out though that `cmd.exe` has even smaller limits, 8192 + // characters [2]. Linkers can often be batch scripts (for example + // Emscripten, Gecko's current build system) which means that we're + // running through batch scripts. These linkers often just forward + // arguments elsewhere (and maybe tack on more), so if we blow 8192 + // bytes we'll typically cause them to blow as well. + // + // Basically as a result just perform an inflated estimate of what our + // command line will look like and test if it's > 8192 (we actually + // test against 6k to artificially inflate our estimate). If all else + // fails we'll fall back to the normal unix logic of testing the OS + // error code if we fail to spawn and automatically re-spawning the + // linker with smaller arguments. + // + // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx + // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 + + let estimated_command_line_len = + self.args.iter().map(|a| a.len()).sum::(); + estimated_command_line_len > 1024 * 6 + } +} + +impl fmt::Debug for Command { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + self.command().fmt(f) + } +} diff --git a/src/librustc_codegen_llvm/back/link.rs b/src/librustc_codegen_llvm/back/link.rs new file mode 100644 index 00000000000..dbfd430a3e2 --- /dev/null +++ b/src/librustc_codegen_llvm/back/link.rs @@ -0,0 +1,1630 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::wasm; +use cc::windows_registry; +use super::archive::{ArchiveBuilder, ArchiveConfig}; +use super::bytecode::RLIB_BYTECODE_EXTENSION; +use super::linker::Linker; +use super::command::Command; +use super::rpath::RPathConfig; +use super::rpath; +use metadata::METADATA_FILENAME; +use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; +use rustc::session::config::{RUST_CGU_EXT, Lto}; +use rustc::session::filesearch; +use rustc::session::search_paths::PathKind; +use rustc::session::Session; +use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind}; +use rustc::middle::dependency_format::Linkage; +use {CodegenResults, CrateInfo}; +use rustc::util::common::time; +use rustc::util::fs::fix_windows_verbatim_for_gcc; +use rustc::hir::def_id::CrateNum; +use tempdir::TempDir; +use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor, TargetTriple}; +use rustc_data_structures::fx::FxHashSet; +use context::get_reloc_model; +use llvm; + +use std::ascii; +use std::char; +use std::env; +use std::fmt; +use std::fs; +use std::io; +use std::path::{Path, PathBuf}; +use std::process::{Output, Stdio}; +use std::str; +use syntax::attr; + +/// The LLVM module name containing crate-metadata. This includes a `.` on +/// purpose, so it cannot clash with the name of a user-defined module. +pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; + +// same as for metadata above, but for allocator shim +pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; + +pub use rustc_codegen_utils::link::{find_crate_name, filename_for_input, default_output_for_target, + invalid_output_for_target, build_link_meta, out_filename, + check_file_is_writeable}; + +// The third parameter is for env vars, used on windows to set up the +// path for MSVC to find its DLLs, and gcc to find its bundled +// toolchain +pub fn get_linker(sess: &Session) -> (PathBuf, Command) { + // If our linker looks like a batch script on Windows then to execute this + // we'll need to spawn `cmd` explicitly. This is primarily done to handle + // emscripten where the linker is `emcc.bat` and needs to be spawned as + // `cmd /c emcc.bat ...`. + // + // This worked historically but is needed manually since #42436 (regression + // was tagged as #42791) and some more info can be found on #44443 for + // emscripten itself. + let cmd = |linker: &Path| { + if let Some(linker) = linker.to_str() { + if cfg!(windows) && linker.ends_with(".bat") { + return Command::bat_script(linker) + } + } + match sess.linker_flavor() { + LinkerFlavor::Lld(f) => Command::lld(linker, f), + _ => Command::new(linker), + + } + }; + + let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); + + let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s) + .or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref())) + .unwrap_or(match sess.linker_flavor() { + LinkerFlavor::Msvc => { + msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref()) + } + LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(), + LinkerFlavor::Em => "emcc".as_ref(), + LinkerFlavor::Gcc => "cc".as_ref(), + LinkerFlavor::Ld => "ld".as_ref(), + LinkerFlavor::Lld(_) => "lld".as_ref(), + }); + + let mut cmd = cmd(linker_path); + + // The compiler's sysroot often has some bundled tools, so add it to the + // PATH for the child. + let mut new_path = sess.host_filesearch(PathKind::All) + .get_tools_search_paths(); + let mut msvc_changed_path = false; + if sess.target.target.options.is_like_msvc { + if let Some(ref tool) = msvc_tool { + cmd.args(tool.args()); + for &(ref k, ref v) in tool.env() { + if k == "PATH" { + new_path.extend(env::split_paths(v)); + msvc_changed_path = true; + } else { + cmd.env(k, v); + } + } + } + } + + if !msvc_changed_path { + if let Some(path) = env::var_os("PATH") { + new_path.extend(env::split_paths(&path)); + } + } + cmd.env("PATH", env::join_paths(new_path).unwrap()); + + (linker_path.to_path_buf(), cmd) +} + +pub fn remove(sess: &Session, path: &Path) { + match fs::remove_file(path) { + Ok(..) => {} + Err(e) => { + sess.err(&format!("failed to remove {}: {}", + path.display(), + e)); + } + } +} + +/// Perform the linkage portion of the compilation phase. This will generate all +/// of the requested outputs for this compilation session. +pub(crate) fn link_binary(sess: &Session, + codegen_results: &CodegenResults, + outputs: &OutputFilenames, + crate_name: &str) -> Vec { + let mut out_filenames = Vec::new(); + for &crate_type in sess.crate_types.borrow().iter() { + // Ignore executable crates if we have -Z no-codegen, as they will error. + let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); + if (sess.opts.debugging_opts.no_codegen || !sess.opts.output_types.should_codegen()) && + !output_metadata && + crate_type == config::CrateTypeExecutable { + continue; + } + + if invalid_output_for_target(sess, crate_type) { + bug!("invalid output type `{:?}` for target os `{}`", + crate_type, sess.opts.target_triple); + } + let mut out_files = link_binary_output(sess, + codegen_results, + crate_type, + outputs, + crate_name); + out_filenames.append(&mut out_files); + } + + // Remove the temporary object file and metadata if we aren't saving temps + if !sess.opts.cg.save_temps { + if sess.opts.output_types.should_codegen() && + !preserve_objects_for_their_debuginfo(sess) + { + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + remove(sess, obj); + } + } + for obj in codegen_results.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { + remove(sess, obj); + } + if let Some(ref obj) = codegen_results.metadata_module.object { + remove(sess, obj); + } + if let Some(ref allocator) = codegen_results.allocator_module { + if let Some(ref obj) = allocator.object { + remove(sess, obj); + } + if let Some(ref bc) = allocator.bytecode_compressed { + remove(sess, bc); + } + } + } + + out_filenames +} + +/// Returns a boolean indicating whether we should preserve the object files on +/// the filesystem for their debug information. This is often useful with +/// split-dwarf like schemes. +fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { + // If the objects don't have debuginfo there's nothing to preserve. + if sess.opts.debuginfo == NoDebugInfo { + return false + } + + // If we're only producing artifacts that are archives, no need to preserve + // the objects as they're losslessly contained inside the archives. + let output_linked = sess.crate_types.borrow() + .iter() + .any(|x| *x != config::CrateTypeRlib && *x != config::CrateTypeStaticlib); + if !output_linked { + return false + } + + // If we're on OSX then the equivalent of split dwarf is turned on by + // default. The final executable won't actually have any debug information + // except it'll have pointers to elsewhere. Historically we've always run + // `dsymutil` to "link all the dwarf together" but this is actually sort of + // a bummer for incremental compilation! (the whole point of split dwarf is + // that you don't do this sort of dwarf link). + // + // Basically as a result this just means that if we're on OSX and we're + // *not* running dsymutil then the object files are the only source of truth + // for debug information, so we must preserve them. + if sess.target.target.options.is_like_osx { + match sess.opts.debugging_opts.run_dsymutil { + // dsymutil is not being run, preserve objects + Some(false) => return true, + + // dsymutil is being run, no need to preserve the objects + Some(true) => return false, + + // The default historical behavior was to always run dsymutil, so + // we're preserving that temporarily, but we're likely to switch the + // default soon. + None => return false, + } + } + + false +} + +fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf { + let out_filename = outputs.single_output_file.clone() + .unwrap_or(outputs + .out_directory + .join(&format!("lib{}{}.rmeta", crate_name, sess.opts.cg.extra_filename))); + check_file_is_writeable(&out_filename, sess); + out_filename +} + +pub(crate) fn each_linked_rlib(sess: &Session, + info: &CrateInfo, + f: &mut FnMut(CrateNum, &Path)) -> Result<(), String> { + let crates = info.used_crates_static.iter(); + let fmts = sess.dependency_formats.borrow(); + let fmts = fmts.get(&config::CrateTypeExecutable) + .or_else(|| fmts.get(&config::CrateTypeStaticlib)) + .or_else(|| fmts.get(&config::CrateTypeCdylib)) + .or_else(|| fmts.get(&config::CrateTypeProcMacro)); + let fmts = match fmts { + Some(f) => f, + None => return Err(format!("could not find formats for rlibs")) + }; + for &(cnum, ref path) in crates { + match fmts.get(cnum.as_usize() - 1) { + Some(&Linkage::NotLinked) | + Some(&Linkage::IncludedFromDylib) => continue, + Some(_) => {} + None => return Err(format!("could not find formats for rlibs")) + } + let name = &info.crate_name[&cnum]; + let path = match *path { + LibSource::Some(ref p) => p, + LibSource::MetadataOnly => { + return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file", + name)) + } + LibSource::None => { + return Err(format!("could not find rlib for: `{}`", name)) + } + }; + f(cnum, &path); + } + Ok(()) +} + +/// Returns a boolean indicating whether the specified crate should be ignored +/// during LTO. +/// +/// Crates ignored during LTO are not lumped together in the "massive object +/// file" that we create and are linked in their normal rlib states. See +/// comments below for what crates do not participate in LTO. +/// +/// It's unusual for a crate to not participate in LTO. Typically only +/// compiler-specific and unstable crates have a reason to not participate in +/// LTO. +pub(crate) fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { + // If our target enables builtin function lowering in LLVM then the + // crates providing these functions don't participate in LTO (e.g. + // no_builtins or compiler builtins crates). + !sess.target.target.options.no_builtins && + (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) +} + +fn link_binary_output(sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) -> Vec { + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + check_file_is_writeable(obj, sess); + } + + let mut out_filenames = vec![]; + + if outputs.outputs.contains_key(&OutputType::Metadata) { + let out_filename = filename_for_metadata(sess, crate_name, outputs); + // To avoid races with another rustc process scanning the output directory, + // we need to write the file somewhere else and atomically move it to its + // final destination, with a `fs::rename` call. In order for the rename to + // always succeed, the temporary file needs to be on the same filesystem, + // which is why we create it inside the output directory specifically. + let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + let metadata = emit_metadata(sess, codegen_results, &metadata_tmpdir); + if let Err(e) = fs::rename(metadata, &out_filename) { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + out_filenames.push(out_filename); + } + + let tmpdir = match TempDir::new("rustc") { + Ok(tmpdir) => tmpdir, + Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), + }; + + if outputs.outputs.should_codegen() { + let out_filename = out_filename(sess, crate_type, outputs, crate_name); + match crate_type { + config::CrateTypeRlib => { + link_rlib(sess, + codegen_results, + RlibFlavor::Normal, + &out_filename, + &tmpdir).build(); + } + config::CrateTypeStaticlib => { + link_staticlib(sess, codegen_results, &out_filename, &tmpdir); + } + _ => { + link_natively(sess, crate_type, &out_filename, codegen_results, tmpdir.path()); + } + } + out_filenames.push(out_filename); + } + + if sess.opts.cg.save_temps { + let _ = tmpdir.into_path(); + } + + out_filenames +} + +fn archive_search_paths(sess: &Session) -> Vec { + let mut search = Vec::new(); + sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { + search.push(path.to_path_buf()); + }); + return search; +} + +fn archive_config<'a>(sess: &'a Session, + output: &Path, + input: Option<&Path>) -> ArchiveConfig<'a> { + ArchiveConfig { + sess, + dst: output.to_path_buf(), + src: input.map(|p| p.to_path_buf()), + lib_search_paths: archive_search_paths(sess), + } +} + +/// We use a temp directory here to avoid races between concurrent rustc processes, +/// such as builds in the same directory using the same filename for metadata while +/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a +/// directory being searched for `extern crate` (observing an incomplete file). +/// The returned path is the temporary file containing the complete metadata. +fn emit_metadata<'a>(sess: &'a Session, codegen_results: &CodegenResults, tmpdir: &TempDir) + -> PathBuf { + let out_filename = tmpdir.path().join(METADATA_FILENAME); + let result = fs::write(&out_filename, &codegen_results.metadata.raw_data); + + if let Err(e) = result { + sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); + } + + out_filename +} + +enum RlibFlavor { + Normal, + StaticlibBase, +} + +// Create an 'rlib' +// +// An rlib in its current incarnation is essentially a renamed .a file. The +// rlib primarily contains the object file of the crate, but it also contains +// all of the object files from native libraries. This is done by unzipping +// native libraries and inserting all of the contents into this archive. +fn link_rlib<'a>(sess: &'a Session, + codegen_results: &CodegenResults, + flavor: RlibFlavor, + out_filename: &Path, + tmpdir: &TempDir) -> ArchiveBuilder<'a> { + info!("preparing rlib to {:?}", out_filename); + let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); + + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + ab.add_file(obj); + } + + // Note that in this loop we are ignoring the value of `lib.cfg`. That is, + // we may not be configured to actually include a static library if we're + // adding it here. That's because later when we consume this rlib we'll + // decide whether we actually needed the static library or not. + // + // To do this "correctly" we'd need to keep track of which libraries added + // which object files to the archive. We don't do that here, however. The + // #[link(cfg(..))] feature is unstable, though, and only intended to get + // liblibc working. In that sense the check below just indicates that if + // there are any libraries we want to omit object files for at link time we + // just exclude all custom object files. + // + // Eventually if we want to stabilize or flesh out the #[link(cfg(..))] + // feature then we'll need to figure out how to record what objects were + // loaded from the libraries found here and then encode that into the + // metadata of the rlib we're generating somehow. + for lib in codegen_results.crate_info.used_libraries.iter() { + match lib.kind { + NativeLibraryKind::NativeStatic => {} + NativeLibraryKind::NativeStaticNobundle | + NativeLibraryKind::NativeFramework | + NativeLibraryKind::NativeUnknown => continue, + } + ab.add_native_library(&lib.name.as_str()); + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. + ab.update_symbols(); + + // Note that it is important that we add all of our non-object "magical + // files" *after* all of the object files in the archive. The reason for + // this is as follows: + // + // * When performing LTO, this archive will be modified to remove + // objects from above. The reason for this is described below. + // + // * When the system linker looks at an archive, it will attempt to + // determine the architecture of the archive in order to see whether its + // linkable. + // + // The algorithm for this detection is: iterate over the files in the + // archive. Skip magical SYMDEF names. Interpret the first file as an + // object file. Read architecture from the object file. + // + // * As one can probably see, if "metadata" and "foo.bc" were placed + // before all of the objects, then the architecture of this archive would + // not be correctly inferred once 'foo.o' is removed. + // + // Basically, all this means is that this code should not move above the + // code above. + match flavor { + RlibFlavor::Normal => { + // Instead of putting the metadata in an object file section, rlibs + // contain the metadata in a separate file. + ab.add_file(&emit_metadata(sess, codegen_results, tmpdir)); + + // For LTO purposes, the bytecode of this library is also inserted + // into the archive. + for bytecode in codegen_results + .modules + .iter() + .filter_map(|m| m.bytecode_compressed.as_ref()) + { + ab.add_file(bytecode); + } + + // After adding all files to the archive, we need to update the + // symbol table of the archive. This currently dies on macOS (see + // #11162), and isn't necessary there anyway + if !sess.target.target.options.is_like_osx { + ab.update_symbols(); + } + } + + RlibFlavor::StaticlibBase => { + let obj = codegen_results.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + ab.add_file(obj); + } + } + } + + ab +} + +// Create a static archive +// +// This is essentially the same thing as an rlib, but it also involves adding +// all of the upstream crates' objects into the archive. This will slurp in +// all of the native libraries of upstream dependencies as well. +// +// Additionally, there's no way for us to link dynamic libraries, so we warn +// about all dynamic library dependencies that they're not linked in. +// +// There's no need to include metadata in a static archive, so ensure to not +// link in the metadata object file (and also don't prepare the archive with a +// metadata file). +fn link_staticlib(sess: &Session, + codegen_results: &CodegenResults, + out_filename: &Path, + tempdir: &TempDir) { + let mut ab = link_rlib(sess, + codegen_results, + RlibFlavor::StaticlibBase, + out_filename, + tempdir); + let mut all_native_libs = vec![]; + + let res = each_linked_rlib(sess, &codegen_results.crate_info, &mut |cnum, path| { + let name = &codegen_results.crate_info.crate_name[&cnum]; + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + + // Here when we include the rlib into our staticlib we need to make a + // decision whether to include the extra object files along the way. + // These extra object files come from statically included native + // libraries, but they may be cfg'd away with #[link(cfg(..))]. + // + // This unstable feature, though, only needs liblibc to work. The only + // use case there is where musl is statically included in liblibc.rlib, + // so if we don't want the included version we just need to skip it. As + // a result the logic here is that if *any* linked library is cfg'd away + // we just skip all object files. + // + // Clearly this is not sufficient for a general purpose feature, and + // we'd want to read from the library's metadata to determine which + // object files come from where and selectively skip them. + let skip_object_files = native_libs.iter().any(|lib| { + lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) + }); + ab.add_rlib(path, + &name.as_str(), + is_full_lto_enabled(sess) && + !ignored_for_lto(sess, &codegen_results.crate_info, cnum), + skip_object_files).unwrap(); + + all_native_libs.extend(codegen_results.crate_info.native_libraries[&cnum].iter().cloned()); + }); + if let Err(e) = res { + sess.fatal(&e); + } + + ab.update_symbols(); + ab.build(); + + if !all_native_libs.is_empty() { + if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { + print_native_static_libs(sess, &all_native_libs); + } + } +} + +fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { + let lib_args: Vec<_> = all_native_libs.iter() + .filter(|l| relevant_lib(sess, l)) + .filter_map(|lib| match lib.kind { + NativeLibraryKind::NativeStaticNobundle | + NativeLibraryKind::NativeUnknown => { + if sess.target.target.options.is_like_msvc { + Some(format!("{}.lib", lib.name)) + } else { + Some(format!("-l{}", lib.name)) + } + }, + NativeLibraryKind::NativeFramework => { + // ld-only syntax, since there are no frameworks in MSVC + Some(format!("-framework {}", lib.name)) + }, + // These are included, no need to print them + NativeLibraryKind::NativeStatic => None, + }) + .collect(); + if !lib_args.is_empty() { + sess.note_without_error("Link against the following native artifacts when linking \ + against this static library. The order and any duplication \ + can be significant on some platforms."); + // Prefix for greppability + sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); + } +} + +// Create a dynamic library or executable +// +// This will invoke the system linker/cc to create the resulting file. This +// links to all upstream files as well. +fn link_natively(sess: &Session, + crate_type: config::CrateType, + out_filename: &Path, + codegen_results: &CodegenResults, + tmpdir: &Path) { + info!("preparing {:?} to {:?}", crate_type, out_filename); + let flavor = sess.linker_flavor(); + + // The invocations of cc share some flags across platforms + let (pname, mut cmd) = get_linker(sess); + + let root = sess.target_filesearch(PathKind::Native).get_lib_path(); + if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { + cmd.args(args); + } + if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { + if sess.crt_static() { + cmd.args(args); + } + } + if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { + cmd.args(args); + } + cmd.args(&sess.opts.debugging_opts.pre_link_arg); + + let pre_link_objects = if crate_type == config::CrateTypeExecutable { + &sess.target.target.options.pre_link_objects_exe + } else { + &sess.target.target.options.pre_link_objects_dll + }; + for obj in pre_link_objects { + cmd.arg(root.join(obj)); + } + + if crate_type == config::CrateTypeExecutable && sess.crt_static() { + for obj in &sess.target.target.options.pre_link_objects_exe_crt { + cmd.arg(root.join(obj)); + } + + for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys { + if flavor == LinkerFlavor::Gcc { + cmd.arg(format!("-l:{}", obj)); + } + } + } + + if sess.target.target.options.is_like_emscripten { + cmd.arg("-s"); + cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { + "DISABLE_EXCEPTION_CATCHING=1" + } else { + "DISABLE_EXCEPTION_CATCHING=0" + }); + } + + { + let mut linker = codegen_results.linker_info.to_linker(cmd, &sess); + link_args(&mut *linker, sess, crate_type, tmpdir, + out_filename, codegen_results); + cmd = linker.finalize(); + } + if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { + cmd.args(args); + } + for obj in &sess.target.target.options.post_link_objects { + cmd.arg(root.join(obj)); + } + if sess.crt_static() { + for obj in &sess.target.target.options.post_link_objects_crt_sys { + if flavor == LinkerFlavor::Gcc { + cmd.arg(format!("-l:{}", obj)); + } + } + for obj in &sess.target.target.options.post_link_objects_crt { + cmd.arg(root.join(obj)); + } + } + if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { + cmd.args(args); + } + for &(ref k, ref v) in &sess.target.target.options.link_env { + cmd.env(k, v); + } + + if sess.opts.debugging_opts.print_link_args { + println!("{:?}", &cmd); + } + + // May have not found libraries in the right formats. + sess.abort_if_errors(); + + // Invoke the system linker + // + // Note that there's a terribly awful hack that really shouldn't be present + // in any compiler. Here an environment variable is supported to + // automatically retry the linker invocation if the linker looks like it + // segfaulted. + // + // Gee that seems odd, normally segfaults are things we want to know about! + // Unfortunately though in rust-lang/rust#38878 we're experiencing the + // linker segfaulting on Travis quite a bit which is causing quite a bit of + // pain to land PRs when they spuriously fail due to a segfault. + // + // The issue #38878 has some more debugging information on it as well, but + // this unfortunately looks like it's just a race condition in macOS's linker + // with some thread pool working in the background. It seems that no one + // currently knows a fix for this so in the meantime we're left with this... + info!("{:?}", &cmd); + let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); + let mut prog; + let mut i = 0; + loop { + i += 1; + prog = time(sess, "running linker", || { + exec_linker(sess, &mut cmd, out_filename, tmpdir) + }); + let output = match prog { + Ok(ref output) => output, + Err(_) => break, + }; + if output.status.success() { + break + } + let mut out = output.stderr.clone(); + out.extend(&output.stdout); + let out = String::from_utf8_lossy(&out); + + // Check to see if the link failed with "unrecognized command line option: + // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, + // reperform the link step without the -no-pie option. This is safe because + // if the linker doesn't support -no-pie then it should not default to + // linking executables as pie. Different versions of gcc seem to use + // different quotes in the error message so don't check for them. + if sess.target.target.options.linker_is_gnu && + sess.linker_flavor() != LinkerFlavor::Ld && + (out.contains("unrecognized command line option") || + out.contains("unknown argument")) && + out.contains("-no-pie") && + cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { + info!("linker output: {:?}", out); + warn!("Linker does not support -no-pie command line option. Retrying without."); + for arg in cmd.take_args() { + if arg.to_string_lossy() != "-no-pie" { + cmd.arg(arg); + } + } + info!("{:?}", &cmd); + continue; + } + if !retry_on_segfault || i > 3 { + break + } + let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; + let msg_bus = "clang: error: unable to execute command: Bus error: 10"; + if !(out.contains(msg_segv) || out.contains(msg_bus)) { + break + } + + warn!( + "looks like the linker segfaulted when we tried to call it, \ + automatically retrying again. cmd = {:?}, out = {}.", + cmd, + out, + ); + } + + match prog { + Ok(prog) => { + fn escape_string(s: &[u8]) -> String { + str::from_utf8(s).map(|s| s.to_owned()) + .unwrap_or_else(|_| { + let mut x = "Non-UTF-8 output: ".to_string(); + x.extend(s.iter() + .flat_map(|&b| ascii::escape_default(b)) + .map(|b| char::from_u32(b as u32).unwrap())); + x + }) + } + if !prog.status.success() { + let mut output = prog.stderr.clone(); + output.extend_from_slice(&prog.stdout); + sess.struct_err(&format!("linking with `{}` failed: {}", + pname.display(), + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(&escape_string(&output)) + .emit(); + sess.abort_if_errors(); + } + info!("linker stderr:\n{}", escape_string(&prog.stderr)); + info!("linker stdout:\n{}", escape_string(&prog.stdout)); + }, + Err(e) => { + let linker_not_found = e.kind() == io::ErrorKind::NotFound; + + let mut linker_error = { + if linker_not_found { + sess.struct_err(&format!("linker `{}` not found", pname.display())) + } else { + sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) + } + }; + + linker_error.note(&format!("{}", e)); + + if !linker_not_found { + linker_error.note(&format!("{:?}", &cmd)); + } + + linker_error.emit(); + + if sess.target.target.options.is_like_msvc && linker_not_found { + sess.note_without_error("the msvc targets depend on the msvc linker \ + but `link.exe` was not found"); + sess.note_without_error("please ensure that VS 2013 or VS 2015 was installed \ + with the Visual C++ option"); + } + sess.abort_if_errors(); + } + } + + + // On macOS, debuggers need this utility to get run to do some munging of + // the symbols. Note, though, that if the object files are being preserved + // for their debug information there's no need for us to run dsymutil. + if sess.target.target.options.is_like_osx && + sess.opts.debuginfo != NoDebugInfo && + !preserve_objects_for_their_debuginfo(sess) + { + match Command::new("dsymutil").arg(out_filename).output() { + Ok(..) => {} + Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), + } + } + + if sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown") { + wasm::rewrite_imports(&out_filename, &codegen_results.crate_info.wasm_imports); + wasm::add_custom_sections(&out_filename, + &codegen_results.crate_info.wasm_custom_sections); + } +} + +fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &Path) + -> io::Result +{ + // When attempting to spawn the linker we run a risk of blowing out the + // size limits for spawning a new process with respect to the arguments + // we pass on the command line. + // + // Here we attempt to handle errors from the OS saying "your list of + // arguments is too big" by reinvoking the linker again with an `@`-file + // that contains all the arguments. The theory is that this is then + // accepted on all linkers and the linker will read all its options out of + // there instead of looking at the command line. + if !cmd.very_likely_to_exceed_some_spawn_limit() { + match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { + Ok(child) => { + let output = child.wait_with_output(); + flush_linked_file(&output, out_filename)?; + return output; + } + Err(ref e) if command_line_too_big(e) => { + info!("command line to linker was too big: {}", e); + } + Err(e) => return Err(e) + } + } + + info!("falling back to passing arguments to linker via an @-file"); + let mut cmd2 = cmd.clone(); + let mut args = String::new(); + for arg in cmd2.take_args() { + args.push_str(&Escape { + arg: arg.to_str().unwrap(), + is_like_msvc: sess.target.target.options.is_like_msvc, + }.to_string()); + args.push_str("\n"); + } + let file = tmpdir.join("linker-arguments"); + let bytes = if sess.target.target.options.is_like_msvc { + let mut out = vec![]; + // start the stream with a UTF-16 BOM + for c in vec![0xFEFF].into_iter().chain(args.encode_utf16()) { + // encode in little endian + out.push(c as u8); + out.push((c >> 8) as u8); + } + out + } else { + args.into_bytes() + }; + fs::write(&file, &bytes)?; + cmd2.arg(format!("@{}", file.display())); + info!("invoking linker {:?}", cmd2); + let output = cmd2.output(); + flush_linked_file(&output, out_filename)?; + return output; + + #[cfg(unix)] + fn flush_linked_file(_: &io::Result, _: &Path) -> io::Result<()> { + Ok(()) + } + + #[cfg(windows)] + fn flush_linked_file(command_output: &io::Result, out_filename: &Path) + -> io::Result<()> + { + // On Windows, under high I/O load, output buffers are sometimes not flushed, + // even long after process exit, causing nasty, non-reproducible output bugs. + // + // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem. + // + // А full writeup of the original Chrome bug can be found at + // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp + + if let &Ok(ref out) = command_output { + if out.status.success() { + if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) { + of.sync_all()?; + } + } + } + + Ok(()) + } + + #[cfg(unix)] + fn command_line_too_big(err: &io::Error) -> bool { + err.raw_os_error() == Some(::libc::E2BIG) + } + + #[cfg(windows)] + fn command_line_too_big(err: &io::Error) -> bool { + const ERROR_FILENAME_EXCED_RANGE: i32 = 206; + err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) + } + + struct Escape<'a> { + arg: &'a str, + is_like_msvc: bool, + } + + impl<'a> fmt::Display for Escape<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + if self.is_like_msvc { + // This is "documented" at + // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx + // + // Unfortunately there's not a great specification of the + // syntax I could find online (at least) but some local + // testing showed that this seemed sufficient-ish to catch + // at least a few edge cases. + write!(f, "\"")?; + for c in self.arg.chars() { + match c { + '"' => write!(f, "\\{}", c)?, + c => write!(f, "{}", c)?, + } + } + write!(f, "\"")?; + } else { + // This is documented at https://linux.die.net/man/1/ld, namely: + // + // > Options in file are separated by whitespace. A whitespace + // > character may be included in an option by surrounding the + // > entire option in either single or double quotes. Any + // > character (including a backslash) may be included by + // > prefixing the character to be included with a backslash. + // + // We put an argument on each line, so all we need to do is + // ensure the line is interpreted as one whole argument. + for c in self.arg.chars() { + match c { + '\\' | + ' ' => write!(f, "\\{}", c)?, + c => write!(f, "{}", c)?, + } + } + } + Ok(()) + } + } +} + +fn link_args(cmd: &mut Linker, + sess: &Session, + crate_type: config::CrateType, + tmpdir: &Path, + out_filename: &Path, + codegen_results: &CodegenResults) { + + // Linker plugins should be specified early in the list of arguments + cmd.cross_lang_lto(); + + // The default library location, we need this to find the runtime. + // The location of crates will be determined as needed. + let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); + + // target descriptor + let t = &sess.target.target; + + cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); + for obj in codegen_results.modules.iter().filter_map(|m| m.object.as_ref()) { + cmd.add_object(obj); + } + cmd.output_filename(out_filename); + + if crate_type == config::CrateTypeExecutable && + sess.target.target.options.is_like_windows { + if let Some(ref s) = codegen_results.windows_subsystem { + cmd.subsystem(s); + } + } + + // If we're building a dynamic library then some platforms need to make sure + // that all symbols are exported correctly from the dynamic library. + if crate_type != config::CrateTypeExecutable || + sess.target.target.options.is_like_emscripten { + cmd.export_symbols(tmpdir, crate_type); + } + + // When linking a dynamic library, we put the metadata into a section of the + // executable. This metadata is in a separate object file from the main + // object file, so we link that in here. + if crate_type == config::CrateTypeDylib || + crate_type == config::CrateTypeProcMacro { + if let Some(obj) = codegen_results.metadata_module.object.as_ref() { + cmd.add_object(obj); + } + } + + let obj = codegen_results.allocator_module + .as_ref() + .and_then(|m| m.object.as_ref()); + if let Some(obj) = obj { + cmd.add_object(obj); + } + + // Try to strip as much out of the generated object by removing unused + // sections if possible. See more comments in linker.rs + if !sess.opts.cg.link_dead_code { + let keep_metadata = crate_type == config::CrateTypeDylib; + cmd.gc_sections(keep_metadata); + } + + let used_link_args = &codegen_results.crate_info.link_args; + + if crate_type == config::CrateTypeExecutable { + let mut position_independent_executable = false; + + if t.options.position_independent_executables { + let empty_vec = Vec::new(); + let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); + let more_args = &sess.opts.cg.link_arg; + let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); + + if get_reloc_model(sess) == llvm::RelocMode::PIC + && !sess.crt_static() && !args.any(|x| *x == "-static") { + position_independent_executable = true; + } + } + + if position_independent_executable { + cmd.position_independent_executable(); + } else { + // recent versions of gcc can be configured to generate position + // independent executables by default. We have to pass -no-pie to + // explicitly turn that off. Not applicable to ld. + if sess.target.target.options.linker_is_gnu + && sess.linker_flavor() != LinkerFlavor::Ld { + cmd.no_position_independent_executable(); + } + } + } + + let relro_level = match sess.opts.debugging_opts.relro_level { + Some(level) => level, + None => t.options.relro_level, + }; + match relro_level { + RelroLevel::Full => { + cmd.full_relro(); + }, + RelroLevel::Partial => { + cmd.partial_relro(); + }, + RelroLevel::Off => { + cmd.no_relro(); + }, + RelroLevel::None => { + }, + } + + // Pass optimization flags down to the linker. + cmd.optimize(); + + // Pass debuginfo flags down to the linker. + cmd.debuginfo(); + + // We want to prevent the compiler from accidentally leaking in any system + // libraries, so we explicitly ask gcc to not link to any libraries by + // default. Note that this does not happen for windows because windows pulls + // in some large number of libraries and I couldn't quite figure out which + // subset we wanted. + if t.options.no_default_libraries { + cmd.no_default_libraries(); + } + + // Take careful note of the ordering of the arguments we pass to the linker + // here. Linkers will assume that things on the left depend on things to the + // right. Things on the right cannot depend on things on the left. This is + // all formally implemented in terms of resolving symbols (libs on the right + // resolve unknown symbols of libs on the left, but not vice versa). + // + // For this reason, we have organized the arguments we pass to the linker as + // such: + // + // 1. The local object that LLVM just generated + // 2. Local native libraries + // 3. Upstream rust libraries + // 4. Upstream native libraries + // + // The rationale behind this ordering is that those items lower down in the + // list can't depend on items higher up in the list. For example nothing can + // depend on what we just generated (e.g. that'd be a circular dependency). + // Upstream rust libraries are not allowed to depend on our local native + // libraries as that would violate the structure of the DAG, in that + // scenario they are required to link to them as well in a shared fashion. + // + // Note that upstream rust libraries may contain native dependencies as + // well, but they also can't depend on what we just started to add to the + // link line. And finally upstream native libraries can't depend on anything + // in this DAG so far because they're only dylibs and dylibs can only depend + // on other dylibs (e.g. other native deps). + add_local_native_libraries(cmd, sess, codegen_results); + add_upstream_rust_crates(cmd, sess, codegen_results, crate_type, tmpdir); + add_upstream_native_libraries(cmd, sess, codegen_results, crate_type); + + // Tell the linker what we're doing. + if crate_type != config::CrateTypeExecutable { + cmd.build_dylib(out_filename); + } + if crate_type == config::CrateTypeExecutable && sess.crt_static() { + cmd.build_static_executable(); + } + + if sess.opts.debugging_opts.pgo_gen.is_some() { + cmd.pgo_gen(); + } + + // FIXME (#2397): At some point we want to rpath our guesses as to + // where extern libraries might live, based on the + // addl_lib_search_paths + if sess.opts.cg.rpath { + let sysroot = sess.sysroot(); + let target_triple = sess.opts.target_triple.triple(); + let mut get_install_prefix_lib_path = || { + let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); + let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); + let mut path = PathBuf::from(install_prefix); + path.push(&tlib); + + path + }; + let mut rpath_config = RPathConfig { + used_crates: &codegen_results.crate_info.used_crates_dynamic, + out_filename: out_filename.to_path_buf(), + has_rpath: sess.target.target.options.has_rpath, + is_like_osx: sess.target.target.options.is_like_osx, + linker_is_gnu: sess.target.target.options.linker_is_gnu, + get_install_prefix_lib_path: &mut get_install_prefix_lib_path, + }; + cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); + } + + // Finally add all the linker arguments provided on the command line along + // with any #[link_args] attributes found inside the crate + if let Some(ref args) = sess.opts.cg.link_args { + cmd.args(args); + } + cmd.args(&sess.opts.cg.link_arg); + cmd.args(&used_link_args); +} + +// # Native library linking +// +// User-supplied library search paths (-L on the command line). These are +// the same paths used to find Rust crates, so some of them may have been +// added already by the previous crate linking code. This only allows them +// to be found at compile time so it is still entirely up to outside +// forces to make sure that library can be found at runtime. +// +// Also note that the native libraries linked here are only the ones located +// in the current crate. Upstream crates with native library dependencies +// may have their native library pulled in above. +fn add_local_native_libraries(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults) { + sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { + match k { + PathKind::Framework => { cmd.framework_path(path); } + _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); } + } + }); + + let relevant_libs = codegen_results.crate_info.used_libraries.iter().filter(|l| { + relevant_lib(sess, l) + }); + + let search_path = archive_search_paths(sess); + for lib in relevant_libs { + match lib.kind { + NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), + NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), + NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()), + NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(), + &search_path) + } + } +} + +// # Rust Crate linking +// +// Rust crates are not considered at all when creating an rlib output. All +// dependencies will be linked when producing the final output (instead of +// the intermediate rlib version) +fn add_upstream_rust_crates(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType, + tmpdir: &Path) { + // All of the heavy lifting has previously been accomplished by the + // dependency_format module of the compiler. This is just crawling the + // output of that module, adding crates as necessary. + // + // Linking to a rlib involves just passing it to the linker (the linker + // will slurp up the object files inside), and linking to a dynamic library + // involves just passing the right -l flag. + + let formats = sess.dependency_formats.borrow(); + let data = formats.get(&crate_type).unwrap(); + + // Invoke get_used_crates to ensure that we get a topological sorting of + // crates. + let deps = &codegen_results.crate_info.used_crates_dynamic; + + // There's a few internal crates in the standard library (aka libcore and + // libstd) which actually have a circular dependence upon one another. This + // currently arises through "weak lang items" where libcore requires things + // like `rust_begin_unwind` but libstd ends up defining it. To get this + // circular dependence to work correctly in all situations we'll need to be + // sure to correctly apply the `--start-group` and `--end-group` options to + // GNU linkers, otherwise if we don't use any other symbol from the standard + // library it'll get discarded and the whole application won't link. + // + // In this loop we're calculating the `group_end`, after which crate to + // pass `--end-group` and `group_start`, before which crate to pass + // `--start-group`. We currently do this by passing `--end-group` after + // the first crate (when iterating backwards) that requires a lang item + // defined somewhere else. Once that's set then when we've defined all the + // necessary lang items we'll pass `--start-group`. + // + // Note that this isn't amazing logic for now but it should do the trick + // for the current implementation of the standard library. + let mut group_end = None; + let mut group_start = None; + let mut end_with = FxHashSet(); + let info = &codegen_results.crate_info; + for &(cnum, _) in deps.iter().rev() { + if let Some(missing) = info.missing_lang_items.get(&cnum) { + end_with.extend(missing.iter().cloned()); + if end_with.len() > 0 && group_end.is_none() { + group_end = Some(cnum); + } + } + end_with.retain(|item| info.lang_item_to_crate.get(item) != Some(&cnum)); + if end_with.len() == 0 && group_end.is_some() { + group_start = Some(cnum); + break + } + } + + // If we didn't end up filling in all lang items from upstream crates then + // we'll be filling it in with our crate. This probably means we're the + // standard library itself, so skip this for now. + if group_end.is_some() && group_start.is_none() { + group_end = None; + } + + let mut compiler_builtins = None; + + for &(cnum, _) in deps.iter() { + if group_start == Some(cnum) { + cmd.group_start(); + } + + // We may not pass all crates through to the linker. Some crates may + // appear statically in an existing dylib, meaning we'll pick up all the + // symbols from the dylib. + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + match data[cnum.as_usize() - 1] { + _ if codegen_results.crate_info.profiler_runtime == Some(cnum) => { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + _ if codegen_results.crate_info.sanitizer_runtime == Some(cnum) => { + link_sanitizer_runtime(cmd, sess, codegen_results, tmpdir, cnum); + } + // compiler-builtins are always placed last to ensure that they're + // linked correctly. + _ if codegen_results.crate_info.compiler_builtins == Some(cnum) => { + assert!(compiler_builtins.is_none()); + compiler_builtins = Some(cnum); + } + Linkage::NotLinked | + Linkage::IncludedFromDylib => {} + Linkage::Static => { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + Linkage::Dynamic => { + add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0) + } + } + + if group_end == Some(cnum) { + cmd.group_end(); + } + } + + // compiler-builtins are always placed last to ensure that they're + // linked correctly. + // We must always link the `compiler_builtins` crate statically. Even if it + // was already "included" in a dylib (e.g. `libstd` when `-C prefer-dynamic` + // is used) + if let Some(cnum) = compiler_builtins { + add_static_crate(cmd, sess, codegen_results, tmpdir, crate_type, cnum); + } + + // Converts a library file-stem into a cc -l argument + fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { + if stem.starts_with("lib") && !config.target.options.is_like_windows { + &stem[3..] + } else { + stem + } + } + + // We must link the sanitizer runtime using -Wl,--whole-archive but since + // it's packed in a .rlib, it contains stuff that are not objects that will + // make the linker error. So we must remove those bits from the .rlib before + // linking it. + fn link_sanitizer_runtime(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + tmpdir: &Path, + cnum: CrateNum) { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let cratepath = &src.rlib.as_ref().unwrap().0; + + if sess.target.target.options.is_like_osx { + // On Apple platforms, the sanitizer is always built as a dylib, and + // LLVM will link to `@rpath/*.dylib`, so we need to specify an + // rpath to the library as well (the rpath should be absolute, see + // PR #41352 for details). + // + // FIXME: Remove this logic into librustc_*san once Cargo supports it + let rpath = cratepath.parent().unwrap(); + let rpath = rpath.to_str().expect("non-utf8 component in path"); + cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); + } + + let dst = tmpdir.join(cratepath.file_name().unwrap()); + let cfg = archive_config(sess, &dst, Some(cratepath)); + let mut archive = ArchiveBuilder::new(cfg); + archive.update_symbols(); + + for f in archive.src_files() { + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + archive.remove_file(&f); + continue + } + } + + archive.build(); + + cmd.link_whole_rlib(&dst); + } + + // Adds the static "rlib" versions of all crates to the command line. + // There's a bit of magic which happens here specifically related to LTO and + // dynamic libraries. Specifically: + // + // * For LTO, we remove upstream object files. + // * For dylibs we remove metadata and bytecode from upstream rlibs + // + // When performing LTO, almost(*) all of the bytecode from the upstream + // libraries has already been included in our object file output. As a + // result we need to remove the object files in the upstream libraries so + // the linker doesn't try to include them twice (or whine about duplicate + // symbols). We must continue to include the rest of the rlib, however, as + // it may contain static native libraries which must be linked in. + // + // (*) Crates marked with `#![no_builtins]` don't participate in LTO and + // their bytecode wasn't included. The object files in those libraries must + // still be passed to the linker. + // + // When making a dynamic library, linkers by default don't include any + // object files in an archive if they're not necessary to resolve the link. + // We basically want to convert the archive (rlib) to a dylib, though, so we + // *do* want everything included in the output, regardless of whether the + // linker thinks it's needed or not. As a result we must use the + // --whole-archive option (or the platform equivalent). When using this + // option the linker will fail if there are non-objects in the archive (such + // as our own metadata and/or bytecode). All in all, for rlibs to be + // entirely included in dylibs, we need to remove all non-object files. + // + // Note, however, that if we're not doing LTO or we're not producing a dylib + // (aka we're making an executable), we can just pass the rlib blindly to + // the linker (fast) because it's fine if it's not actually included as + // we're at the end of the dependency chain. + fn add_static_crate(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + tmpdir: &Path, + crate_type: config::CrateType, + cnum: CrateNum) { + let src = &codegen_results.crate_info.used_crate_source[&cnum]; + let cratepath = &src.rlib.as_ref().unwrap().0; + + // See the comment above in `link_staticlib` and `link_rlib` for why if + // there's a static library that's not relevant we skip all object + // files. + let native_libs = &codegen_results.crate_info.native_libraries[&cnum]; + let skip_native = native_libs.iter().any(|lib| { + lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) + }); + + if (!is_full_lto_enabled(sess) || + ignored_for_lto(sess, &codegen_results.crate_info, cnum)) && + crate_type != config::CrateTypeDylib && + !skip_native { + cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); + return + } + + let dst = tmpdir.join(cratepath.file_name().unwrap()); + let name = cratepath.file_name().unwrap().to_str().unwrap(); + let name = &name[3..name.len() - 5]; // chop off lib/.rlib + + time(sess, &format!("altering {}.rlib", name), || { + let cfg = archive_config(sess, &dst, Some(cratepath)); + let mut archive = ArchiveBuilder::new(cfg); + archive.update_symbols(); + + let mut any_objects = false; + for f in archive.src_files() { + if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { + archive.remove_file(&f); + continue + } + + let canonical = f.replace("-", "_"); + let canonical_name = name.replace("-", "_"); + + // Look for `.rcgu.o` at the end of the filename to conclude + // that this is a Rust-related object file. + fn looks_like_rust(s: &str) -> bool { + let path = Path::new(s); + let ext = path.extension().and_then(|s| s.to_str()); + if ext != Some(OutputType::Object.extension()) { + return false + } + let ext2 = path.file_stem() + .and_then(|s| Path::new(s).extension()) + .and_then(|s| s.to_str()); + ext2 == Some(RUST_CGU_EXT) + } + + let is_rust_object = + canonical.starts_with(&canonical_name) && + looks_like_rust(&f); + + // If we've been requested to skip all native object files + // (those not generated by the rust compiler) then we can skip + // this file. See above for why we may want to do this. + let skip_because_cfg_say_so = skip_native && !is_rust_object; + + // If we're performing LTO and this is a rust-generated object + // file, then we don't need the object file as it's part of the + // LTO module. Note that `#![no_builtins]` is excluded from LTO, + // though, so we let that object file slide. + let skip_because_lto = is_full_lto_enabled(sess) && + is_rust_object && + (sess.target.target.options.no_builtins || + !codegen_results.crate_info.is_no_builtins.contains(&cnum)); + + if skip_because_cfg_say_so || skip_because_lto { + archive.remove_file(&f); + } else { + any_objects = true; + } + } + + if !any_objects { + return + } + archive.build(); + + // If we're creating a dylib, then we need to include the + // whole of each object in our archive into that artifact. This is + // because a `dylib` can be reused as an intermediate artifact. + // + // Note, though, that we don't want to include the whole of a + // compiler-builtins crate (e.g. compiler-rt) because it'll get + // repeatedly linked anyway. + if crate_type == config::CrateTypeDylib && + codegen_results.crate_info.compiler_builtins != Some(cnum) { + cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); + } else { + cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst)); + } + }); + } + + // Same thing as above, but for dynamic crates instead of static crates. + fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { + // If we're performing LTO, then it should have been previously required + // that all upstream rust dependencies were available in an rlib format. + assert!(!is_full_lto_enabled(sess)); + + // Just need to tell the linker about where the library lives and + // what its name is + let parent = cratepath.parent(); + if let Some(dir) = parent { + cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); + } + let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); + cmd.link_rust_dylib(&unlib(&sess.target, filestem), + parent.unwrap_or(Path::new(""))); + } +} + +// Link in all of our upstream crates' native dependencies. Remember that +// all of these upstream native dependencies are all non-static +// dependencies. We've got two cases then: +// +// 1. The upstream crate is an rlib. In this case we *must* link in the +// native dependency because the rlib is just an archive. +// +// 2. The upstream crate is a dylib. In order to use the dylib, we have to +// have the dependency present on the system somewhere. Thus, we don't +// gain a whole lot from not linking in the dynamic dependency to this +// crate as well. +// +// The use case for this is a little subtle. In theory the native +// dependencies of a crate are purely an implementation detail of the crate +// itself, but the problem arises with generic and inlined functions. If a +// generic function calls a native function, then the generic function must +// be instantiated in the target crate, meaning that the native symbol must +// also be resolved in the target crate. +fn add_upstream_native_libraries(cmd: &mut Linker, + sess: &Session, + codegen_results: &CodegenResults, + crate_type: config::CrateType) { + // Be sure to use a topological sorting of crates because there may be + // interdependencies between native libraries. When passing -nodefaultlibs, + // for example, almost all native libraries depend on libc, so we have to + // make sure that's all the way at the right (liblibc is near the base of + // the dependency chain). + // + // This passes RequireStatic, but the actual requirement doesn't matter, + // we're just getting an ordering of crate numbers, we're not worried about + // the paths. + let formats = sess.dependency_formats.borrow(); + let data = formats.get(&crate_type).unwrap(); + + let crates = &codegen_results.crate_info.used_crates_static; + for &(cnum, _) in crates { + for lib in codegen_results.crate_info.native_libraries[&cnum].iter() { + if !relevant_lib(sess, &lib) { + continue + } + match lib.kind { + NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), + NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), + NativeLibraryKind::NativeStaticNobundle => { + // Link "static-nobundle" native libs only if the crate they originate from + // is being linked statically to the current crate. If it's linked dynamically + // or is an rlib already included via some other dylib crate, the symbols from + // native libs will have already been included in that dylib. + if data[cnum.as_usize() - 1] == Linkage::Static { + cmd.link_staticlib(&lib.name.as_str()) + } + }, + // ignore statically included native libraries here as we've + // already included them when we included the rust library + // previously + NativeLibraryKind::NativeStatic => {} + } + } + } +} + +fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { + match lib.cfg { + Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), + None => true, + } +} + +fn is_full_lto_enabled(sess: &Session) -> bool { + match sess.lto() { + Lto::Yes | + Lto::Thin | + Lto::Fat => true, + Lto::No | + Lto::ThinLocal => false, + } +} diff --git a/src/librustc_codegen_llvm/back/linker.rs b/src/librustc_codegen_llvm/back/linker.rs new file mode 100644 index 00000000000..dd1983bdc17 --- /dev/null +++ b/src/librustc_codegen_llvm/back/linker.rs @@ -0,0 +1,1037 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; +use std::ffi::{OsStr, OsString}; +use std::fs::{self, File}; +use std::io::prelude::*; +use std::io::{self, BufWriter}; +use std::path::{Path, PathBuf}; + +use back::archive; +use back::command::Command; +use back::symbol_export; +use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; +use rustc::middle::dependency_format::Linkage; +use rustc::session::Session; +use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, + CrossLangLto}; +use rustc::ty::TyCtxt; +use rustc_target::spec::{LinkerFlavor, LldFlavor}; +use serialize::{json, Encoder}; + +/// For all the linkers we support, and information they might +/// need out of the shared crate context before we get rid of it. +pub struct LinkerInfo { + exports: HashMap>, +} + +impl LinkerInfo { + pub fn new(tcx: TyCtxt) -> LinkerInfo { + LinkerInfo { + exports: tcx.sess.crate_types.borrow().iter().map(|&c| { + (c, exported_symbols(tcx, c)) + }).collect(), + } + } + + pub fn to_linker<'a>(&'a self, + cmd: Command, + sess: &'a Session) -> Box { + match sess.linker_flavor() { + LinkerFlavor::Lld(LldFlavor::Link) | + LinkerFlavor::Msvc => { + Box::new(MsvcLinker { + cmd, + sess, + info: self + }) as Box + } + LinkerFlavor::Em => { + Box::new(EmLinker { + cmd, + sess, + info: self + }) as Box + } + LinkerFlavor::Gcc => { + Box::new(GccLinker { + cmd, + sess, + info: self, + hinted_static: false, + is_ld: false, + }) as Box + } + + LinkerFlavor::Lld(LldFlavor::Ld) | + LinkerFlavor::Lld(LldFlavor::Ld64) | + LinkerFlavor::Ld => { + Box::new(GccLinker { + cmd, + sess, + info: self, + hinted_static: false, + is_ld: true, + }) as Box + } + + LinkerFlavor::Lld(LldFlavor::Wasm) => { + Box::new(WasmLd { + cmd, + }) as Box + } + } + } +} + +/// Linker abstraction used by back::link to build up the command to invoke a +/// linker. +/// +/// This trait is the total list of requirements needed by `back::link` and +/// represents the meaning of each option being passed down. This trait is then +/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an +/// MSVC linker (e.g. `link.exe`) is being used. +pub trait Linker { + fn link_dylib(&mut self, lib: &str); + fn link_rust_dylib(&mut self, lib: &str, path: &Path); + fn link_framework(&mut self, framework: &str); + fn link_staticlib(&mut self, lib: &str); + fn link_rlib(&mut self, lib: &Path); + fn link_whole_rlib(&mut self, lib: &Path); + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); + fn include_path(&mut self, path: &Path); + fn framework_path(&mut self, path: &Path); + fn output_filename(&mut self, path: &Path); + fn add_object(&mut self, path: &Path); + fn gc_sections(&mut self, keep_metadata: bool); + fn position_independent_executable(&mut self); + fn no_position_independent_executable(&mut self); + fn full_relro(&mut self); + fn partial_relro(&mut self); + fn no_relro(&mut self); + fn optimize(&mut self); + fn pgo_gen(&mut self); + fn debuginfo(&mut self); + fn no_default_libraries(&mut self); + fn build_dylib(&mut self, out_filename: &Path); + fn build_static_executable(&mut self); + fn args(&mut self, args: &[String]); + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); + fn subsystem(&mut self, subsystem: &str); + fn group_start(&mut self); + fn group_end(&mut self); + fn cross_lang_lto(&mut self); + // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). + fn finalize(&mut self) -> Command; +} + +pub struct GccLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo, + hinted_static: bool, // Keeps track of the current hinting mode. + // Link as ld + is_ld: bool, +} + +impl<'a> GccLinker<'a> { + /// Argument that must be passed *directly* to the linker + /// + /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used + fn linker_arg(&mut self, arg: S) -> &mut Self + where S: AsRef + { + if !self.is_ld { + let mut os = OsString::from("-Wl,"); + os.push(arg.as_ref()); + self.cmd.arg(os); + } else { + self.cmd.arg(arg); + } + self + } + + fn takes_hints(&self) -> bool { + !self.sess.target.target.options.is_like_osx + } + + // Some platforms take hints about whether a library is static or dynamic. + // For those that support this, we ensure we pass the option if the library + // was flagged "static" (most defaults are dynamic) to ensure that if + // libfoo.a and libfoo.so both exist that the right one is chosen. + fn hint_static(&mut self) { + if !self.takes_hints() { return } + if !self.hinted_static { + self.linker_arg("-Bstatic"); + self.hinted_static = true; + } + } + + fn hint_dynamic(&mut self) { + if !self.takes_hints() { return } + if self.hinted_static { + self.linker_arg("-Bdynamic"); + self.hinted_static = false; + } + } +} + +impl<'a> Linker for GccLinker<'a> { + fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); } + fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); } + fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); } + fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } + fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } + fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } + fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } + fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } + fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } + fn no_relro(&mut self) { self.linker_arg("-z,norelro"); } + fn build_static_executable(&mut self) { self.cmd.arg("-static"); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.hint_dynamic(); + self.cmd.arg("-l").arg(lib); + } + + fn link_framework(&mut self, framework: &str) { + self.hint_dynamic(); + self.cmd.arg("-framework").arg(framework); + } + + // Here we explicitly ask that the entire archive is included into the + // result artifact. For more details see #15460, but the gist is that + // the linker will strip away any unused objects in the archive if we + // don't otherwise explicitly reference them. This can occur for + // libraries which are just providing bindings, libraries with generic + // functions, etc. + fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { + self.hint_static(); + let target = &self.sess.target.target; + if !target.options.is_like_osx { + self.linker_arg("--whole-archive").cmd.arg("-l").arg(lib); + self.linker_arg("--no-whole-archive"); + } else { + // -force_load is the macOS equivalent of --whole-archive, but it + // involves passing the full path to the library to link. + let mut v = OsString::from("-force_load,"); + v.push(&archive::find_library(lib, search_path, &self.sess)); + self.linker_arg(&v); + } + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.hint_static(); + if self.sess.target.target.options.is_like_osx { + let mut v = OsString::from("-force_load,"); + v.push(lib); + self.linker_arg(&v); + } else { + self.linker_arg("--whole-archive").cmd.arg(lib); + self.linker_arg("--no-whole-archive"); + } + } + + fn gc_sections(&mut self, keep_metadata: bool) { + // The dead_strip option to the linker specifies that functions and data + // unreachable by the entry point will be removed. This is quite useful + // with Rust's compilation model of compiling libraries at a time into + // one object file. For example, this brings hello world from 1.7MB to + // 458K. + // + // Note that this is done for both executables and dynamic libraries. We + // won't get much benefit from dylibs because LLVM will have already + // stripped away as much as it could. This has not been seen to impact + // link times negatively. + // + // -dead_strip can't be part of the pre_link_args because it's also used + // for partial linking when using multiple codegen units (-r). So we + // insert it here. + if self.sess.target.target.options.is_like_osx { + self.linker_arg("-dead_strip"); + } else if self.sess.target.target.options.is_like_solaris { + self.linker_arg("-z"); + self.linker_arg("ignore"); + + // If we're building a dylib, we don't use --gc-sections because LLVM + // has already done the best it can do, and we also don't want to + // eliminate the metadata. If we're building an executable, however, + // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% + // reduction. + } else if !keep_metadata { + self.linker_arg("--gc-sections"); + } + } + + fn optimize(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // GNU-style linkers support optimization with -O. GNU ld doesn't + // need a numeric argument, but other linkers do. + if self.sess.opts.optimize == config::OptLevel::Default || + self.sess.opts.optimize == config::OptLevel::Aggressive { + self.linker_arg("-O1"); + } + } + + fn pgo_gen(&mut self) { + if !self.sess.target.target.options.linker_is_gnu { return } + + // If we're doing PGO generation stuff and on a GNU-like linker, use the + // "-u" flag to properly pull in the profiler runtime bits. + // + // This is because LLVM otherwise won't add the needed initialization + // for us on Linux (though the extra flag should be harmless if it + // does). + // + // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030. + // + // Though it may be worth to try to revert those changes upstream, since + // the overhead of the initialization should be minor. + self.cmd.arg("-u"); + self.cmd.arg("__llvm_profile_runtime"); + } + + fn debuginfo(&mut self) { + match self.sess.opts.debuginfo { + DebugInfoLevel::NoDebugInfo => { + // If we are building without debuginfo enabled and we were called with + // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo + // found when linking to get rid of symbols from libstd. + match self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { + Some(true) => { self.linker_arg("-S"); }, + _ => {}, + } + }, + _ => {}, + }; + } + + fn no_default_libraries(&mut self) { + if !self.is_ld { + self.cmd.arg("-nodefaultlibs"); + } + } + + fn build_dylib(&mut self, out_filename: &Path) { + // On mac we need to tell the linker to let this library be rpathed + if self.sess.target.target.options.is_like_osx { + self.cmd.arg("-dynamiclib"); + self.linker_arg("-dylib"); + + // Note that the `osx_rpath_install_name` option here is a hack + // purely to support rustbuild right now, we should get a more + // principled solution at some point to force the compiler to pass + // the right `-Wl,-install_name` with an `@rpath` in it. + if self.sess.opts.cg.rpath || + self.sess.opts.debugging_opts.osx_rpath_install_name { + let mut v = OsString::from("-install_name,@rpath/"); + v.push(out_filename.file_name().unwrap()); + self.linker_arg(&v); + } + } else { + self.cmd.arg("-shared"); + } + } + + fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { + // If we're compiling a dylib, then we let symbol visibility in object + // files to take care of whether they're exported or not. + // + // If we're compiling a cdylib, however, we manually create a list of + // exported symbols to ensure we don't expose any more. The object files + // have far more public symbols than we actually want to export, so we + // hide them all here. + if crate_type == CrateType::CrateTypeDylib || + crate_type == CrateType::CrateTypeProcMacro { + return + } + + let mut arg = OsString::new(); + let path = tmpdir.join("list"); + + debug!("EXPORTED SYMBOLS:"); + + if self.sess.target.target.options.is_like_osx { + // Write a plain, newline-separated list of symbols + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + for sym in self.info.exports[&crate_type].iter() { + debug!(" _{}", sym); + writeln!(f, "_{}", sym)?; + } + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + } + } else { + // Write an LD version script + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + writeln!(f, "{{\n global:")?; + for sym in self.info.exports[&crate_type].iter() { + debug!(" {};", sym); + writeln!(f, " {};", sym)?; + } + writeln!(f, "\n local:\n *;\n}};")?; + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write version script: {}", e)); + } + } + + if self.sess.target.target.options.is_like_osx { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("-exported_symbols_list,"); + } else if self.sess.target.target.options.is_like_solaris { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("-M,"); + } else { + if !self.is_ld { + arg.push("-Wl,") + } + arg.push("--version-script="); + } + + arg.push(&path); + self.cmd.arg(arg); + } + + fn subsystem(&mut self, subsystem: &str) { + self.linker_arg(&format!("--subsystem,{}", subsystem)); + } + + fn finalize(&mut self) -> Command { + self.hint_dynamic(); // Reset to default before returning the composed command line. + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + fn group_start(&mut self) { + if !self.sess.target.target.options.is_like_osx { + self.linker_arg("--start-group"); + } + } + + fn group_end(&mut self) { + if !self.sess.target.target.options.is_like_osx { + self.linker_arg("--end-group"); + } + } + + fn cross_lang_lto(&mut self) { + match self.sess.opts.debugging_opts.cross_lang_lto { + CrossLangLto::Disabled | + CrossLangLto::NoLink => { + // Nothing to do + } + CrossLangLto::LinkerPlugin(ref path) => { + self.linker_arg(&format!("-plugin={}", path.display())); + + let opt_level = match self.sess.opts.optimize { + config::OptLevel::No => "O0", + config::OptLevel::Less => "O1", + config::OptLevel::Default => "O2", + config::OptLevel::Aggressive => "O3", + config::OptLevel::Size => "Os", + config::OptLevel::SizeMin => "Oz", + }; + + self.linker_arg(&format!("-plugin-opt={}", opt_level)); + self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); + + match self.sess.opts.cg.lto { + config::Lto::Thin | + config::Lto::ThinLocal => { + self.linker_arg(&format!("-plugin-opt=thin")); + } + config::Lto::Fat | + config::Lto::Yes | + config::Lto::No => { + // default to regular LTO + } + } + } + } + } +} + +pub struct MsvcLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo +} + +impl<'a> Linker for MsvcLinker<'a> { + fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } + fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } + fn args(&mut self, args: &[String]) { self.cmd.args(args); } + + fn build_dylib(&mut self, out_filename: &Path) { + self.cmd.arg("/DLL"); + let mut arg: OsString = "/IMPLIB:".into(); + arg.push(out_filename.with_extension("dll.lib")); + self.cmd.arg(arg); + } + + fn build_static_executable(&mut self) { + // noop + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + // MSVC's ICF (Identical COMDAT Folding) link optimization is + // slow for Rust and thus we disable it by default when not in + // optimization build. + if self.sess.opts.optimize != config::OptLevel::No { + self.cmd.arg("/OPT:REF,ICF"); + } else { + // It is necessary to specify NOICF here, because /OPT:REF + // implies ICF by default. + self.cmd.arg("/OPT:REF,NOICF"); + } + } + + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn link_rust_dylib(&mut self, lib: &str, path: &Path) { + // When producing a dll, the MSVC linker may not actually emit a + // `foo.lib` file if the dll doesn't actually export any symbols, so we + // check to see if the file is there and just omit linking to it if it's + // not present. + let name = format!("{}.dll.lib", lib); + if fs::metadata(&path.join(&name)).is_ok() { + self.cmd.arg(name); + } + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg(&format!("{}.lib", lib)); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_position_independent_executable(&mut self) { + // noop + } + + fn full_relro(&mut self) { + // noop + } + + fn partial_relro(&mut self) { + // noop + } + + fn no_relro(&mut self) { + // noop + } + + fn no_default_libraries(&mut self) { + // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC + // as there's been trouble in the past of linking the C++ standard + // library required by LLVM. This likely needs to happen one day, but + // in general Windows is also a more controlled environment than + // Unix, so it's not necessarily as critical that this be implemented. + // + // Note that there are also some licensing worries about statically + // linking some libraries which require a specific agreement, so it may + // not ever be possible for us to pass this flag. + } + + fn include_path(&mut self, path: &Path) { + let mut arg = OsString::from("/LIBPATH:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn output_filename(&mut self, path: &Path) { + let mut arg = OsString::from("/OUT:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn framework_path(&mut self, _path: &Path) { + bug!("frameworks are not supported on windows") + } + fn link_framework(&mut self, _framework: &str) { + bug!("frameworks are not supported on windows") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + fn link_whole_rlib(&mut self, path: &Path) { + // not supported? + self.link_rlib(path); + } + fn optimize(&mut self) { + // Needs more investigation of `/OPT` arguments + } + + fn pgo_gen(&mut self) { + // Nothing needed here. + } + + fn debuginfo(&mut self) { + // This will cause the Microsoft linker to generate a PDB file + // from the CodeView line tables in the object files. + self.cmd.arg("/DEBUG"); + + // This will cause the Microsoft linker to embed .natvis info into the the PDB file + let sysroot = self.sess.sysroot(); + let natvis_dir_path = sysroot.join("lib\\rustlib\\etc"); + if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { + // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes + // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore + // them, eventually mooting this workaround, per this landed patch: + // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100 + if let Some(ref linker_path) = self.sess.opts.cg.linker { + if let Some(linker_name) = Path::new(&linker_path).file_stem() { + if linker_name.to_str().unwrap().to_lowercase() == "lld-link" { + self.sess.warn("not embedding natvis: lld-link may not support the flag"); + return; + } + } + } + for entry in natvis_dir { + match entry { + Ok(entry) => { + let path = entry.path(); + if path.extension() == Some("natvis".as_ref()) { + let mut arg = OsString::from("/NATVIS:"); + arg.push(path); + self.cmd.arg(arg); + } + }, + Err(err) => { + self.sess.warn(&format!("error enumerating natvis directory: {}", err)); + }, + } + } + } + } + + // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to + // export symbols from a dynamic library. When building a dynamic library, + // however, we're going to want some symbols exported, so this function + // generates a DEF file which lists all the symbols. + // + // The linker will read this `*.def` file and export all the symbols from + // the dynamic library. Note that this is not as simple as just exporting + // all the symbols in the current crate (as specified by `codegen.reachable`) + // but rather we also need to possibly export the symbols of upstream + // crates. Upstream rlibs may be linked statically to this dynamic library, + // in which case they may continue to transitively be used and hence need + // their symbols exported. + fn export_symbols(&mut self, + tmpdir: &Path, + crate_type: CrateType) { + let path = tmpdir.join("lib.def"); + let res = (|| -> io::Result<()> { + let mut f = BufWriter::new(File::create(&path)?); + + // Start off with the standard module name header and then go + // straight to exports. + writeln!(f, "LIBRARY")?; + writeln!(f, "EXPORTS")?; + for symbol in self.info.exports[&crate_type].iter() { + debug!(" _{}", symbol); + writeln!(f, " {}", symbol)?; + } + Ok(()) + })(); + if let Err(e) = res { + self.sess.fatal(&format!("failed to write lib.def file: {}", e)); + } + let mut arg = OsString::from("/DEF:"); + arg.push(path); + self.cmd.arg(&arg); + } + + fn subsystem(&mut self, subsystem: &str) { + // Note that previous passes of the compiler validated this subsystem, + // so we just blindly pass it to the linker. + self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); + + // Windows has two subsystems we're interested in right now, the console + // and windows subsystems. These both implicitly have different entry + // points (starting symbols). The console entry point starts with + // `mainCRTStartup` and the windows entry point starts with + // `WinMainCRTStartup`. These entry points, defined in system libraries, + // will then later probe for either `main` or `WinMain`, respectively to + // start the application. + // + // In Rust we just always generate a `main` function so we want control + // to always start there, so we force the entry point on the windows + // subsystem to be `mainCRTStartup` to get everything booted up + // correctly. + // + // For more information see RFC #1665 + if subsystem == "windows" { + self.cmd.arg("/ENTRY:mainCRTStartup"); + } + } + + fn finalize(&mut self) -> Command { + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // MSVC doesn't need group indicators + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } +} + +pub struct EmLinker<'a> { + cmd: Command, + sess: &'a Session, + info: &'a LinkerInfo +} + +impl<'a> Linker for EmLinker<'a> { + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn link_dylib(&mut self, lib: &str) { + // Emscripten always links statically + self.link_staticlib(lib); + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + // not supported? + self.link_staticlib(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + // not supported? + self.link_rlib(lib); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.link_dylib(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.add_object(lib); + } + + fn position_independent_executable(&mut self) { + // noop + } + + fn no_position_independent_executable(&mut self) { + // noop + } + + fn full_relro(&mut self) { + // noop + } + + fn partial_relro(&mut self) { + // noop + } + + fn no_relro(&mut self) { + // noop + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn framework_path(&mut self, _path: &Path) { + bug!("frameworks are not supported on Emscripten") + } + + fn link_framework(&mut self, _framework: &str) { + bug!("frameworks are not supported on Emscripten") + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + // noop + } + + fn optimize(&mut self) { + // Emscripten performs own optimizations + self.cmd.arg(match self.sess.opts.optimize { + OptLevel::No => "-O0", + OptLevel::Less => "-O1", + OptLevel::Default => "-O2", + OptLevel::Aggressive => "-O3", + OptLevel::Size => "-Os", + OptLevel::SizeMin => "-Oz" + }); + // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved + self.cmd.args(&["--memory-init-file", "0"]); + } + + fn pgo_gen(&mut self) { + // noop, but maybe we need something like the gnu linker? + } + + fn debuginfo(&mut self) { + // Preserve names or generate source maps depending on debug info + self.cmd.arg(match self.sess.opts.debuginfo { + DebugInfoLevel::NoDebugInfo => "-g0", + DebugInfoLevel::LimitedDebugInfo => "-g3", + DebugInfoLevel::FullDebugInfo => "-g4" + }); + } + + fn no_default_libraries(&mut self) { + self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); + } + + fn build_dylib(&mut self, _out_filename: &Path) { + bug!("building dynamic library is unsupported on Emscripten") + } + + fn build_static_executable(&mut self) { + // noop + } + + fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { + let symbols = &self.info.exports[&crate_type]; + + debug!("EXPORTED SYMBOLS:"); + + self.cmd.arg("-s"); + + let mut arg = OsString::from("EXPORTED_FUNCTIONS="); + let mut encoded = String::new(); + + { + let mut encoder = json::Encoder::new(&mut encoded); + let res = encoder.emit_seq(symbols.len(), |encoder| { + for (i, sym) in symbols.iter().enumerate() { + encoder.emit_seq_elt(i, |encoder| { + encoder.emit_str(&("_".to_string() + sym)) + })?; + } + Ok(()) + }); + if let Err(e) = res { + self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); + } + } + debug!("{}", encoded); + arg.push(encoded); + + self.cmd.arg(arg); + } + + fn subsystem(&mut self, _subsystem: &str) { + // noop + } + + fn finalize(&mut self) -> Command { + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // Appears not necessary on Emscripten + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing + } +} + +fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { + let mut symbols = Vec::new(); + + let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); + for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { + if level.is_below_threshold(export_threshold) { + symbols.push(symbol.symbol_name(tcx).to_string()); + } + } + + let formats = tcx.sess.dependency_formats.borrow(); + let deps = formats[&crate_type].iter(); + + for (index, dep_format) in deps.enumerate() { + let cnum = CrateNum::new(index + 1); + // For each dependency that we are linking to statically ... + if *dep_format == Linkage::Static { + // ... we add its symbol list to our export list. + for &(symbol, level) in tcx.exported_symbols(cnum).iter() { + if level.is_below_threshold(export_threshold) { + symbols.push(symbol.symbol_name(tcx).to_string()); + } + } + } + } + + symbols +} + +pub struct WasmLd { + cmd: Command, +} + +impl Linker for WasmLd { + fn link_dylib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_staticlib(&mut self, lib: &str) { + self.cmd.arg("-l").arg(lib); + } + + fn link_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn include_path(&mut self, path: &Path) { + self.cmd.arg("-L").arg(path); + } + + fn framework_path(&mut self, _path: &Path) { + panic!("frameworks not supported") + } + + fn output_filename(&mut self, path: &Path) { + self.cmd.arg("-o").arg(path); + } + + fn add_object(&mut self, path: &Path) { + self.cmd.arg(path); + } + + fn position_independent_executable(&mut self) { + } + + fn full_relro(&mut self) { + } + + fn partial_relro(&mut self) { + } + + fn no_relro(&mut self) { + } + + fn build_static_executable(&mut self) { + } + + fn args(&mut self, args: &[String]) { + self.cmd.args(args); + } + + fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { + self.cmd.arg("-l").arg(lib); + } + + fn link_framework(&mut self, _framework: &str) { + panic!("frameworks not supported") + } + + fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { + self.cmd.arg("-l").arg(lib); + } + + fn link_whole_rlib(&mut self, lib: &Path) { + self.cmd.arg(lib); + } + + fn gc_sections(&mut self, _keep_metadata: bool) { + } + + fn optimize(&mut self) { + } + + fn pgo_gen(&mut self) { + } + + fn debuginfo(&mut self) { + } + + fn no_default_libraries(&mut self) { + } + + fn build_dylib(&mut self, _out_filename: &Path) { + } + + fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) { + } + + fn subsystem(&mut self, _subsystem: &str) { + } + + fn no_position_independent_executable(&mut self) { + } + + fn finalize(&mut self) -> Command { + // There have been reports in the wild (rustwasm/wasm-bindgen#119) of + // using threads causing weird hangs and bugs. Disable it entirely as + // this isn't yet the bottleneck of compilation at all anyway. + self.cmd.arg("--no-threads"); + + self.cmd.arg("-z").arg("stack-size=1048576"); + + // FIXME we probably shouldn't pass this but instead pass an explicit + // whitelist of symbols we'll allow to be undefined. Unfortunately + // though we can't handle symbols like `log10` that LLVM injects at a + // super late date without actually parsing object files. For now let's + // stick to this and hopefully fix it before stabilization happens. + self.cmd.arg("--allow-undefined"); + + // For now we just never have an entry symbol + self.cmd.arg("--no-entry"); + + let mut cmd = Command::new(""); + ::std::mem::swap(&mut cmd, &mut self.cmd); + cmd + } + + // Not needed for now with LLD + fn group_start(&mut self) {} + fn group_end(&mut self) {} + + fn cross_lang_lto(&mut self) { + // Do nothing for now + } +} diff --git a/src/librustc_codegen_llvm/back/lto.rs b/src/librustc_codegen_llvm/back/lto.rs new file mode 100644 index 00000000000..96eda50d788 --- /dev/null +++ b/src/librustc_codegen_llvm/back/lto.rs @@ -0,0 +1,773 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; +use back::symbol_export; +use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; +use back::write; +use errors::{FatalError, Handler}; +use llvm::archive_ro::ArchiveRO; +use llvm::{ModuleRef, TargetMachineRef, True, False}; +use llvm; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::middle::exported_symbols::SymbolExportLevel; +use rustc::session::config::{self, Lto}; +use rustc::util::common::time_ext; +use time_graph::Timeline; +use {ModuleCodegen, ModuleLlvm, ModuleKind, ModuleSource}; + +use libc; + +use std::ffi::CString; +use std::ptr; +use std::slice; +use std::sync::Arc; + +pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { + match crate_type { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeCdylib => true, + + config::CrateTypeDylib | + config::CrateTypeRlib | + config::CrateTypeProcMacro => false, + } +} + +pub(crate) enum LtoModuleCodegen { + Fat { + module: Option, + _serialized_bitcode: Vec, + }, + + Thin(ThinModule), +} + +impl LtoModuleCodegen { + pub fn name(&self) -> &str { + match *self { + LtoModuleCodegen::Fat { .. } => "everything", + LtoModuleCodegen::Thin(ref m) => m.name(), + } + } + + /// Optimize this module within the given codegen context. + /// + /// This function is unsafe as it'll return a `ModuleCodegen` still + /// points to LLVM data structures owned by this `LtoModuleCodegen`. + /// It's intended that the module returned is immediately code generated and + /// dropped, and then this LTO module is dropped. + pub(crate) unsafe fn optimize(&mut self, + cgcx: &CodegenContext, + timeline: &mut Timeline) + -> Result + { + match *self { + LtoModuleCodegen::Fat { ref mut module, .. } => { + let module = module.take().unwrap(); + let config = cgcx.config(module.kind); + let llmod = module.llvm().unwrap().llmod; + let tm = module.llvm().unwrap().tm; + run_pass_manager(cgcx, tm, llmod, config, false); + timeline.record("fat-done"); + Ok(module) + } + LtoModuleCodegen::Thin(ref mut thin) => thin.optimize(cgcx, timeline), + } + } + + /// A "gauge" of how costly it is to optimize this module, used to sort + /// biggest modules first. + pub fn cost(&self) -> u64 { + match *self { + // Only one module with fat LTO, so the cost doesn't matter. + LtoModuleCodegen::Fat { .. } => 0, + LtoModuleCodegen::Thin(ref m) => m.cost(), + } + } +} + +pub(crate) fn run(cgcx: &CodegenContext, + modules: Vec, + timeline: &mut Timeline) + -> Result, FatalError> +{ + let diag_handler = cgcx.create_diag_handler(); + let export_threshold = match cgcx.lto { + // We're just doing LTO for our one crate + Lto::ThinLocal => SymbolExportLevel::Rust, + + // We're doing LTO for the entire crate graph + Lto::Yes | Lto::Fat | Lto::Thin => { + symbol_export::crates_export_threshold(&cgcx.crate_types) + } + + Lto::No => panic!("didn't request LTO but we're doing LTO"), + }; + + let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { + if level.is_below_threshold(export_threshold) { + let mut bytes = Vec::with_capacity(name.len() + 1); + bytes.extend(name.bytes()); + Some(CString::new(bytes).unwrap()) + } else { + None + } + }; + let exported_symbols = cgcx.exported_symbols + .as_ref().expect("needs exported symbols for LTO"); + let mut symbol_white_list = exported_symbols[&LOCAL_CRATE] + .iter() + .filter_map(symbol_filter) + .collect::>(); + timeline.record("whitelist"); + info!("{} symbols to preserve in this crate", symbol_white_list.len()); + + // If we're performing LTO for the entire crate graph, then for each of our + // upstream dependencies, find the corresponding rlib and load the bitcode + // from the archive. + // + // We save off all the bytecode and LLVM module ids for later processing + // with either fat or thin LTO + let mut upstream_modules = Vec::new(); + if cgcx.lto != Lto::ThinLocal { + if cgcx.opts.cg.prefer_dynamic { + diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") + .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ + supported with LTO") + .emit(); + return Err(FatalError) + } + + // Make sure we actually can run LTO + for crate_type in cgcx.crate_types.iter() { + if !crate_type_allows_lto(*crate_type) { + let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ + static library outputs"); + return Err(e) + } + } + + for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { + let exported_symbols = cgcx.exported_symbols + .as_ref().expect("needs exported symbols for LTO"); + symbol_white_list.extend( + exported_symbols[&cnum] + .iter() + .filter_map(symbol_filter)); + + let archive = ArchiveRO::open(&path).expect("wanted an rlib"); + let bytecodes = archive.iter().filter_map(|child| { + child.ok().and_then(|c| c.name().map(|name| (name, c))) + }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); + for (name, data) in bytecodes { + info!("adding bytecode {}", name); + let bc_encoded = data.data(); + + let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || { + match DecodedBytecode::new(bc_encoded) { + Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), + Err(e) => Err(diag_handler.fatal(&e)), + } + })?; + let bc = SerializedModule::FromRlib(bc); + upstream_modules.push((bc, CString::new(id).unwrap())); + } + timeline.record(&format!("load: {}", path.display())); + } + } + + let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::>(); + match cgcx.lto { + Lto::Yes | // `-C lto` == fat LTO by default + Lto::Fat => { + fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) + } + Lto::Thin | + Lto::ThinLocal => { + thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) + } + Lto::No => unreachable!(), + } +} + +fn fat_lto(cgcx: &CodegenContext, + diag_handler: &Handler, + mut modules: Vec, + mut serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + info!("going for a fat lto"); + + // Find the "costliest" module and merge everything into that codegen unit. + // All the other modules will be serialized and reparsed into the new + // context, so this hopefully avoids serializing and parsing the largest + // codegen unit. + // + // Additionally use a regular module as the base here to ensure that various + // file copy operations in the backend work correctly. The only other kind + // of module here should be an allocator one, and if your crate is smaller + // than the allocator module then the size doesn't really matter anyway. + let (_, costliest_module) = modules.iter() + .enumerate() + .filter(|&(_, module)| module.kind == ModuleKind::Regular) + .map(|(i, module)| { + let cost = unsafe { + llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) + }; + (cost, i) + }) + .max() + .expect("must be codegen'ing at least one module"); + let module = modules.remove(costliest_module); + let llmod = module.llvm().expect("can't lto pre-codegened modules").llmod; + info!("using {:?} as a base module", module.llmod_id); + + // For all other modules we codegened we'll need to link them into our own + // bitcode. All modules were codegened in their own LLVM context, however, + // and we want to move everything to the same LLVM context. Currently the + // way we know of to do that is to serialize them to a string and them parse + // them later. Not great but hey, that's why it's "fat" LTO, right? + for module in modules { + let llvm = module.llvm().expect("can't lto pre-codegened modules"); + let buffer = ModuleBuffer::new(llvm.llmod); + let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); + serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); + } + + // For all serialized bitcode files we parse them and link them in as we did + // above, this is all mostly handled in C++. Like above, though, we don't + // know much about the memory management here so we err on the side of being + // save and persist everything with the original module. + let mut serialized_bitcode = Vec::new(); + let mut linker = Linker::new(llmod); + for (bc_decoded, name) in serialized_modules { + info!("linking {:?}", name); + time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { + let data = bc_decoded.data(); + linker.add(&data).map_err(|()| { + let msg = format!("failed to load bc of {:?}", name); + write::llvm_err(&diag_handler, msg) + }) + })?; + timeline.record(&format!("link {:?}", name)); + serialized_bitcode.push(bc_decoded); + } + drop(linker); + cgcx.save_temp_bitcode(&module, "lto.input"); + + // Internalize everything that *isn't* in our whitelist to help strip out + // more modules and such + unsafe { + let ptr = symbol_white_list.as_ptr(); + llvm::LLVMRustRunRestrictionPass(llmod, + ptr as *const *const libc::c_char, + symbol_white_list.len() as libc::size_t); + cgcx.save_temp_bitcode(&module, "lto.after-restriction"); + } + + if cgcx.no_landing_pads { + unsafe { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + } + cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); + } + timeline.record("passes"); + + Ok(vec![LtoModuleCodegen::Fat { + module: Some(module), + _serialized_bitcode: serialized_bitcode, + }]) +} + +struct Linker(llvm::LinkerRef); + +impl Linker { + fn new(llmod: ModuleRef) -> Linker { + unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } + } + + fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { + unsafe { + if llvm::LLVMRustLinkerAdd(self.0, + bytecode.as_ptr() as *const libc::c_char, + bytecode.len()) { + Ok(()) + } else { + Err(()) + } + } + } +} + +impl Drop for Linker { + fn drop(&mut self) { + unsafe { llvm::LLVMRustLinkerFree(self.0); } + } +} + +/// Prepare "thin" LTO to get run on these modules. +/// +/// The general structure of ThinLTO is quite different from the structure of +/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into +/// one giant LLVM module, and then we run more optimization passes over this +/// big module after internalizing most symbols. Thin LTO, on the other hand, +/// avoid this large bottleneck through more targeted optimization. +/// +/// At a high level Thin LTO looks like: +/// +/// 1. Prepare a "summary" of each LLVM module in question which describes +/// the values inside, cost of the values, etc. +/// 2. Merge the summaries of all modules in question into one "index" +/// 3. Perform some global analysis on this index +/// 4. For each module, use the index and analysis calculated previously to +/// perform local transformations on the module, for example inlining +/// small functions from other modules. +/// 5. Run thin-specific optimization passes over each module, and then code +/// generate everything at the end. +/// +/// The summary for each module is intended to be quite cheap, and the global +/// index is relatively quite cheap to create as well. As a result, the goal of +/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more +/// situations. For example one cheap optimization is that we can parallelize +/// all codegen modules, easily making use of all the cores on a machine. +/// +/// With all that in mind, the function here is designed at specifically just +/// calculating the *index* for ThinLTO. This index will then be shared amongst +/// all of the `LtoModuleCodegen` units returned below and destroyed once +/// they all go out of scope. +fn thin_lto(diag_handler: &Handler, + modules: Vec, + serialized_modules: Vec<(SerializedModule, CString)>, + symbol_white_list: &[*const libc::c_char], + timeline: &mut Timeline) + -> Result, FatalError> +{ + unsafe { + info!("going for that thin, thin LTO"); + + let mut thin_buffers = Vec::new(); + let mut module_names = Vec::new(); + let mut thin_modules = Vec::new(); + + // FIXME: right now, like with fat LTO, we serialize all in-memory + // modules before working with them and ThinLTO. We really + // shouldn't do this, however, and instead figure out how to + // extract a summary from an in-memory module and then merge that + // into the global index. It turns out that this loop is by far + // the most expensive portion of this small bit of global + // analysis! + for (i, module) in modules.iter().enumerate() { + info!("local module: {} - {}", i, module.llmod_id); + let llvm = module.llvm().expect("can't lto precodegened module"); + let name = CString::new(module.llmod_id.clone()).unwrap(); + let buffer = ThinBuffer::new(llvm.llmod); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: buffer.data().as_ptr(), + len: buffer.data().len(), + }); + thin_buffers.push(buffer); + module_names.push(name); + timeline.record(&module.llmod_id); + } + + // FIXME: All upstream crates are deserialized internally in the + // function below to extract their summary and modules. Note that + // unlike the loop above we *must* decode and/or read something + // here as these are all just serialized files on disk. An + // improvement, however, to make here would be to store the + // module summary separately from the actual module itself. Right + // now this is store in one large bitcode file, and the entire + // file is deflate-compressed. We could try to bypass some of the + // decompression by storing the index uncompressed and only + // lazily decompressing the bytecode if necessary. + // + // Note that truly taking advantage of this optimization will + // likely be further down the road. We'd have to implement + // incremental ThinLTO first where we could actually avoid + // looking at upstream modules entirely sometimes (the contents, + // we must always unconditionally look at the index). + let mut serialized = Vec::new(); + for (module, name) in serialized_modules { + info!("foreign module {:?}", name); + thin_modules.push(llvm::ThinLTOModule { + identifier: name.as_ptr(), + data: module.data().as_ptr(), + len: module.data().len(), + }); + serialized.push(module); + module_names.push(name); + } + + // Delegate to the C++ bindings to create some data here. Once this is a + // tried-and-true interface we may wish to try to upstream some of this + // to LLVM itself, right now we reimplement a lot of what they do + // upstream... + let data = llvm::LLVMRustCreateThinLTOData( + thin_modules.as_ptr(), + thin_modules.len() as u32, + symbol_white_list.as_ptr(), + symbol_white_list.len() as u32, + ); + if data.is_null() { + let msg = format!("failed to prepare thin LTO context"); + return Err(write::llvm_err(&diag_handler, msg)) + } + let data = ThinData(data); + info!("thin LTO data created"); + timeline.record("data"); + + // Throw our data in an `Arc` as we'll be sharing it across threads. We + // also put all memory referenced by the C++ data (buffers, ids, etc) + // into the arc as well. After this we'll create a thin module + // codegen per module in this data. + let shared = Arc::new(ThinShared { + data, + thin_buffers, + serialized_modules: serialized, + module_names, + }); + Ok((0..shared.module_names.len()).map(|i| { + LtoModuleCodegen::Thin(ThinModule { + shared: shared.clone(), + idx: i, + }) + }).collect()) + } +} + +fn run_pass_manager(cgcx: &CodegenContext, + tm: TargetMachineRef, + llmod: ModuleRef, + config: &ModuleConfig, + thin: bool) { + // Now we have one massive module inside of llmod. Time to run the + // LTO-specific optimization passes that LLVM provides. + // + // This code is based off the code found in llvm's LTO code generator: + // tools/lto/LTOCodeGenerator.cpp + debug!("running the pass manager"); + unsafe { + let pm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); + + // When optimizing for LTO we don't actually pass in `-O0`, but we force + // it to always happen at least with `-O1`. + // + // With ThinLTO we mess around a lot with symbol visibility in a way + // that will actually cause linking failures if we optimize at O0 which + // notable is lacking in dead code elimination. To ensure we at least + // get some optimizations and correctly link we forcibly switch to `-O1` + // to get dead code elimination. + // + // Note that in general this shouldn't matter too much as you typically + // only turn on ThinLTO when you're compiling with optimizations + // otherwise. + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + let opt_level = match opt_level { + llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less, + level => level, + }; + with_llvm_pmb(llmod, config, opt_level, false, &mut |b| { + if thin { + if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { + panic!("this version of LLVM does not support ThinLTO"); + } + } else { + llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, + /* Internalize = */ False, + /* RunInliner = */ True); + } + }); + + let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); + assert!(!pass.is_null()); + llvm::LLVMRustAddPass(pm, pass); + + time_ext(cgcx.time_passes, None, "LTO passes", || + llvm::LLVMRunPassManager(pm, llmod)); + + llvm::LLVMDisposePassManager(pm); + } + debug!("lto done"); +} + +pub enum SerializedModule { + Local(ModuleBuffer), + FromRlib(Vec), +} + +impl SerializedModule { + fn data(&self) -> &[u8] { + match *self { + SerializedModule::Local(ref m) => m.data(), + SerializedModule::FromRlib(ref m) => m, + } + } +} + +pub struct ModuleBuffer(*mut llvm::ModuleBuffer); + +unsafe impl Send for ModuleBuffer {} +unsafe impl Sync for ModuleBuffer {} + +impl ModuleBuffer { + pub fn new(m: ModuleRef) -> ModuleBuffer { + ModuleBuffer(unsafe { + llvm::LLVMRustModuleBufferCreate(m) + }) + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustModuleBufferPtr(self.0); + let len = llvm::LLVMRustModuleBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ModuleBuffer { + fn drop(&mut self) { + unsafe { llvm::LLVMRustModuleBufferFree(self.0); } + } +} + +pub struct ThinModule { + shared: Arc, + idx: usize, +} + +struct ThinShared { + data: ThinData, + thin_buffers: Vec, + serialized_modules: Vec, + module_names: Vec, +} + +struct ThinData(*mut llvm::ThinLTOData); + +unsafe impl Send for ThinData {} +unsafe impl Sync for ThinData {} + +impl Drop for ThinData { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustFreeThinLTOData(self.0); + } + } +} + +pub struct ThinBuffer(*mut llvm::ThinLTOBuffer); + +unsafe impl Send for ThinBuffer {} +unsafe impl Sync for ThinBuffer {} + +impl ThinBuffer { + pub fn new(m: ModuleRef) -> ThinBuffer { + unsafe { + let buffer = llvm::LLVMRustThinLTOBufferCreate(m); + ThinBuffer(buffer) + } + } + + pub fn data(&self) -> &[u8] { + unsafe { + let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; + let len = llvm::LLVMRustThinLTOBufferLen(self.0); + slice::from_raw_parts(ptr, len) + } + } +} + +impl Drop for ThinBuffer { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustThinLTOBufferFree(self.0); + } + } +} + +impl ThinModule { + fn name(&self) -> &str { + self.shared.module_names[self.idx].to_str().unwrap() + } + + fn cost(&self) -> u64 { + // Yes, that's correct, we're using the size of the bytecode as an + // indicator for how costly this codegen unit is. + self.data().len() as u64 + } + + fn data(&self) -> &[u8] { + let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); + a.unwrap_or_else(|| { + let len = self.shared.thin_buffers.len(); + self.shared.serialized_modules[self.idx - len].data() + }) + } + + unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) + -> Result + { + let diag_handler = cgcx.create_diag_handler(); + let tm = (cgcx.tm_factory)().map_err(|e| { + write::llvm_err(&diag_handler, e) + })?; + + // Right now the implementation we've got only works over serialized + // modules, so we create a fresh new LLVM context and parse the module + // into that context. One day, however, we may do this for upstream + // crates but for locally codegened modules we may be able to reuse + // that LLVM Context and Module. + let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); + let llmod = llvm::LLVMRustParseBitcodeForThinLTO( + llcx, + self.data().as_ptr(), + self.data().len(), + self.shared.module_names[self.idx].as_ptr(), + ); + if llmod.is_null() { + let msg = format!("failed to parse bitcode for thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)); + } + let module = ModuleCodegen { + source: ModuleSource::Codegened(ModuleLlvm { + llmod, + llcx, + tm, + }), + llmod_id: self.name().to_string(), + name: self.name().to_string(), + kind: ModuleKind::Regular, + }; + cgcx.save_temp_bitcode(&module, "thin-lto-input"); + + // Before we do much else find the "main" `DICompileUnit` that we'll be + // using below. If we find more than one though then rustc has changed + // in a way we're not ready for, so generate an ICE by returning + // an error. + let mut cu1 = ptr::null_mut(); + let mut cu2 = ptr::null_mut(); + llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); + if !cu2.is_null() { + let msg = format!("multiple source DICompileUnits found"); + return Err(write::llvm_err(&diag_handler, msg)) + } + + // Like with "fat" LTO, get some better optimizations if landing pads + // are disabled by removing all landing pads. + if cgcx.no_landing_pads { + llvm::LLVMRustMarkAllFunctionsNounwind(llmod); + cgcx.save_temp_bitcode(&module, "thin-lto-after-nounwind"); + timeline.record("nounwind"); + } + + // Up next comes the per-module local analyses that we do for Thin LTO. + // Each of these functions is basically copied from the LLVM + // implementation and then tailored to suit this implementation. Ideally + // each of these would be supported by upstream LLVM but that's perhaps + // a patch for another day! + // + // You can find some more comments about these functions in the LLVM + // bindings we've got (currently `PassWrapper.cpp`) + if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-rename"); + timeline.record("rename"); + if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-resolve"); + timeline.record("resolve"); + if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-internalize"); + timeline.record("internalize"); + if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { + let msg = format!("failed to prepare thin LTO module"); + return Err(write::llvm_err(&diag_handler, msg)) + } + cgcx.save_temp_bitcode(&module, "thin-lto-after-import"); + timeline.record("import"); + + // Ok now this is a bit unfortunate. This is also something you won't + // find upstream in LLVM's ThinLTO passes! This is a hack for now to + // work around bugs in LLVM. + // + // First discovered in #45511 it was found that as part of ThinLTO + // importing passes LLVM will import `DICompileUnit` metadata + // information across modules. This means that we'll be working with one + // LLVM module that has multiple `DICompileUnit` instances in it (a + // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of + // bugs in LLVM's backend which generates invalid DWARF in a situation + // like this: + // + // https://bugs.llvm.org/show_bug.cgi?id=35212 + // https://bugs.llvm.org/show_bug.cgi?id=35562 + // + // While the first bug there is fixed the second ended up causing #46346 + // which was basically a resurgence of #45511 after LLVM's bug 35212 was + // fixed. + // + // This function below is a huge hack around this problem. The function + // below is defined in `PassWrapper.cpp` and will basically "merge" + // all `DICompileUnit` instances in a module. Basically it'll take all + // the objects, rewrite all pointers of `DISubprogram` to point to the + // first `DICompileUnit`, and then delete all the other units. + // + // This is probably mangling to the debug info slightly (but hopefully + // not too much) but for now at least gets LLVM to emit valid DWARF (or + // so it appears). Hopefully we can remove this once upstream bugs are + // fixed in LLVM. + llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); + cgcx.save_temp_bitcode(&module, "thin-lto-after-patch"); + timeline.record("patch"); + + // Alright now that we've done everything related to the ThinLTO + // analysis it's time to run some optimizations! Here we use the same + // `run_pass_manager` as the "fat" LTO above except that we tell it to + // populate a thin-specific pass manager, which presumably LLVM treats a + // little differently. + info!("running thin lto passes over {}", module.name); + let config = cgcx.config(module.kind); + run_pass_manager(cgcx, tm, llmod, config, true); + cgcx.save_temp_bitcode(&module, "thin-lto-after-pm"); + timeline.record("thin-done"); + + // FIXME: this is a hack around a bug in LLVM right now. Discovered in + // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen + // error if there's an available_externally function in the LLVM module. + // Typically we don't actually use these functions but ThinLTO makes + // heavy use of them when inlining across modules. + // + // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this + // function call (and its definition on the C++ side of things) + // shouldn't be necessary eventually and we can safetly delete these few + // lines. + llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); + cgcx.save_temp_bitcode(&module, "thin-lto-after-rm-ae"); + timeline.record("no-ae"); + + Ok(module) + } +} diff --git a/src/librustc_codegen_llvm/back/rpath.rs b/src/librustc_codegen_llvm/back/rpath.rs new file mode 100644 index 00000000000..8e5e7d37648 --- /dev/null +++ b/src/librustc_codegen_llvm/back/rpath.rs @@ -0,0 +1,282 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashSet; +use std::env; +use std::path::{Path, PathBuf}; +use std::fs; + +use rustc::hir::def_id::CrateNum; +use rustc::middle::cstore::LibSource; + +pub struct RPathConfig<'a> { + pub used_crates: &'a [(CrateNum, LibSource)], + pub out_filename: PathBuf, + pub is_like_osx: bool, + pub has_rpath: bool, + pub linker_is_gnu: bool, + pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf, +} + +pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { + // No rpath on windows + if !config.has_rpath { + return Vec::new(); + } + + let mut flags = Vec::new(); + + debug!("preparing the RPATH!"); + + let libs = config.used_crates.clone(); + let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); + let rpaths = get_rpaths(config, &libs); + flags.extend_from_slice(&rpaths_to_flags(&rpaths)); + + // Use DT_RUNPATH instead of DT_RPATH if available + if config.linker_is_gnu { + flags.push("-Wl,--enable-new-dtags".to_string()); + } + + flags +} + +fn rpaths_to_flags(rpaths: &[String]) -> Vec { + let mut ret = Vec::new(); + for rpath in rpaths { + if rpath.contains(',') { + ret.push("-Wl,-rpath".into()); + ret.push("-Xlinker".into()); + ret.push(rpath.clone()); + } else { + ret.push(format!("-Wl,-rpath,{}", &(*rpath))); + } + } + return ret; +} + +fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec { + debug!("output: {:?}", config.out_filename.display()); + debug!("libs:"); + for libpath in libs { + debug!(" {:?}", libpath.display()); + } + + // Use relative paths to the libraries. Binaries can be moved + // as long as they maintain the relative relationship to the + // crates they depend on. + let rel_rpaths = get_rpaths_relative_to_output(config, libs); + + // And a final backup rpath to the global library location. + let fallback_rpaths = vec![get_install_prefix_rpath(config)]; + + fn log_rpaths(desc: &str, rpaths: &[String]) { + debug!("{} rpaths:", desc); + for rpath in rpaths { + debug!(" {}", *rpath); + } + } + + log_rpaths("relative", &rel_rpaths); + log_rpaths("fallback", &fallback_rpaths); + + let mut rpaths = rel_rpaths; + rpaths.extend_from_slice(&fallback_rpaths); + + // Remove duplicates + let rpaths = minimize_rpaths(&rpaths); + return rpaths; +} + +fn get_rpaths_relative_to_output(config: &mut RPathConfig, + libs: &[PathBuf]) -> Vec { + libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() +} + +fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String { + // Mac doesn't appear to support $ORIGIN + let prefix = if config.is_like_osx { + "@loader_path" + } else { + "$ORIGIN" + }; + + let cwd = env::current_dir().unwrap(); + let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); + lib.pop(); + let mut output = cwd.join(&config.out_filename); + output.pop(); + let output = fs::canonicalize(&output).unwrap_or(output); + let relative = path_relative_from(&lib, &output) + .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib)); + // FIXME (#9639): This needs to handle non-utf8 paths + format!("{}/{}", prefix, + relative.to_str().expect("non-utf8 component in path")) +} + +// This routine is adapted from the *old* Path's `path_relative_from` +// function, which works differently from the new `relative_from` function. +// In particular, this handles the case on unix where both paths are +// absolute but with only the root as the common directory. +fn path_relative_from(path: &Path, base: &Path) -> Option { + use std::path::Component; + + if path.is_absolute() != base.is_absolute() { + if path.is_absolute() { + Some(PathBuf::from(path)) + } else { + None + } + } else { + let mut ita = path.components(); + let mut itb = base.components(); + let mut comps: Vec = vec![]; + loop { + match (ita.next(), itb.next()) { + (None, None) => break, + (Some(a), None) => { + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + (None, _) => comps.push(Component::ParentDir), + (Some(a), Some(b)) if comps.is_empty() && a == b => (), + (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), + (Some(_), Some(b)) if b == Component::ParentDir => return None, + (Some(a), Some(_)) => { + comps.push(Component::ParentDir); + for _ in itb { + comps.push(Component::ParentDir); + } + comps.push(a); + comps.extend(ita.by_ref()); + break; + } + } + } + Some(comps.iter().map(|c| c.as_os_str()).collect()) + } +} + + +fn get_install_prefix_rpath(config: &mut RPathConfig) -> String { + let path = (config.get_install_prefix_lib_path)(); + let path = env::current_dir().unwrap().join(&path); + // FIXME (#9639): This needs to handle non-utf8 paths + path.to_str().expect("non-utf8 component in rpath").to_string() +} + +fn minimize_rpaths(rpaths: &[String]) -> Vec { + let mut set = HashSet::new(); + let mut minimized = Vec::new(); + for rpath in rpaths { + if set.insert(rpath) { + minimized.push(rpath.clone()); + } + } + minimized +} + +#[cfg(all(unix, test))] +mod tests { + use super::{RPathConfig}; + use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; + use std::path::{Path, PathBuf}; + + #[test] + fn test_rpaths_to_flags() { + let flags = rpaths_to_flags(&[ + "path1".to_string(), + "path2".to_string() + ]); + assert_eq!(flags, + ["-Wl,-rpath,path1", + "-Wl,-rpath,path2"]); + } + + #[test] + fn test_minimize1() { + let res = minimize_rpaths(&[ + "rpath1".to_string(), + "rpath2".to_string(), + "rpath1".to_string() + ]); + assert!(res == [ + "rpath1", + "rpath2", + ]); + } + + #[test] + fn test_minimize2() { + let res = minimize_rpaths(&[ + "1a".to_string(), + "2".to_string(), + "2".to_string(), + "1a".to_string(), + "4a".to_string(), + "1a".to_string(), + "2".to_string(), + "3".to_string(), + "4a".to_string(), + "3".to_string() + ]); + assert!(res == [ + "1a", + "2", + "4a", + "3", + ]); + } + + #[test] + fn test_rpath_relative() { + if cfg!(target_os = "macos") { + let config = &mut RPathConfig { + used_crates: Vec::new(), + has_rpath: true, + is_like_osx: true, + linker_is_gnu: false, + out_filename: PathBuf::from("bin/rustc"), + get_install_prefix_lib_path: &mut || panic!(), + }; + let res = get_rpath_relative_to_output(config, + Path::new("lib/libstd.so")); + assert_eq!(res, "@loader_path/../lib"); + } else { + let config = &mut RPathConfig { + used_crates: Vec::new(), + out_filename: PathBuf::from("bin/rustc"), + get_install_prefix_lib_path: &mut || panic!(), + has_rpath: true, + is_like_osx: false, + linker_is_gnu: true, + }; + let res = get_rpath_relative_to_output(config, + Path::new("lib/libstd.so")); + assert_eq!(res, "$ORIGIN/../lib"); + } + } + + #[test] + fn test_xlinker() { + let args = rpaths_to_flags(&[ + "a/normal/path".to_string(), + "a,comma,path".to_string() + ]); + + assert_eq!(args, vec![ + "-Wl,-rpath,a/normal/path".to_string(), + "-Wl,-rpath".to_string(), + "-Xlinker".to_string(), + "a,comma,path".to_string() + ]); + } +} diff --git a/src/librustc_codegen_llvm/back/symbol_export.rs b/src/librustc_codegen_llvm/back/symbol_export.rs new file mode 100644 index 00000000000..81ac684aee2 --- /dev/null +++ b/src/librustc_codegen_llvm/back/symbol_export.rs @@ -0,0 +1,396 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc_data_structures::sync::Lrc; +use std::sync::Arc; + +use monomorphize::Instance; +use rustc::hir; +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; +use rustc::ich::Fingerprint; +use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; +use rustc::session::config; +use rustc::ty::{TyCtxt, SymbolName}; +use rustc::ty::maps::Providers; +use rustc::ty::subst::Substs; +use rustc::util::nodemap::{FxHashMap, DefIdMap}; +use rustc_allocator::ALLOCATOR_METHODS; +use rustc_data_structures::indexed_vec::IndexVec; +use std::collections::hash_map::Entry::*; + +pub type ExportedSymbols = FxHashMap< + CrateNum, + Arc>, +>; + +pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { + crates_export_threshold(&tcx.sess.crate_types.borrow()) +} + +fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { + match crate_type { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeProcMacro | + config::CrateTypeCdylib => SymbolExportLevel::C, + config::CrateTypeRlib | + config::CrateTypeDylib => SymbolExportLevel::Rust, + } +} + +pub fn crates_export_threshold(crate_types: &[config::CrateType]) + -> SymbolExportLevel { + if crate_types.iter().any(|&crate_type| { + crate_export_threshold(crate_type) == SymbolExportLevel::Rust + }) { + SymbolExportLevel::Rust + } else { + SymbolExportLevel::C + } +} + +fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Lrc> +{ + assert_eq!(cnum, LOCAL_CRATE); + + if !tcx.sess.opts.output_types.should_codegen() { + return Lrc::new(DefIdMap()) + } + + // Check to see if this crate is a "special runtime crate". These + // crates, implementation details of the standard library, typically + // have a bunch of `pub extern` and `#[no_mangle]` functions as the + // ABI between them. We don't want their symbols to have a `C` + // export level, however, as they're just implementation details. + // Down below we'll hardwire all of the symbols to the `Rust` export + // level instead. + let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || + tcx.is_compiler_builtins(LOCAL_CRATE); + + let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0 + .iter() + .filter_map(|&node_id| { + // We want to ignore some FFI functions that are not exposed from + // this crate. Reachable FFI functions can be lumped into two + // categories: + // + // 1. Those that are included statically via a static library + // 2. Those included otherwise (e.g. dynamically or via a framework) + // + // Although our LLVM module is not literally emitting code for the + // statically included symbols, it's an export of our library which + // needs to be passed on to the linker and encoded in the metadata. + // + // As a result, if this id is an FFI item (foreign item) then we only + // let it through if it's included statically. + match tcx.hir.get(node_id) { + hir::map::NodeForeignItem(..) => { + let def_id = tcx.hir.local_def_id(node_id); + if tcx.is_statically_included_foreign_item(def_id) { + Some(def_id) + } else { + None + } + } + + // Only consider nodes that actually have exported symbols. + hir::map::NodeItem(&hir::Item { + node: hir::ItemStatic(..), + .. + }) | + hir::map::NodeItem(&hir::Item { + node: hir::ItemFn(..), .. + }) | + hir::map::NodeImplItem(&hir::ImplItem { + node: hir::ImplItemKind::Method(..), + .. + }) => { + let def_id = tcx.hir.local_def_id(node_id); + let generics = tcx.generics_of(def_id); + if !generics.requires_monomorphization(tcx) && + // Functions marked with #[inline] are only ever codegened + // with "internal" linkage and are never exported. + !Instance::mono(tcx, def_id).def.requires_local(tcx) { + Some(def_id) + } else { + None + } + } + + _ => None + } + }) + .map(|def_id| { + let export_level = if special_runtime_crate { + let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str(); + // We can probably do better here by just ensuring that + // it has hidden visibility rather than public + // visibility, as this is primarily here to ensure it's + // not stripped during LTO. + // + // In general though we won't link right if these + // symbols are stripped, and LTO currently strips them. + if &*name == "rust_eh_personality" || + &*name == "rust_eh_register_frames" || + &*name == "rust_eh_unregister_frames" { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } + } else { + symbol_export_level(tcx, def_id) + }; + debug!("EXPORTED SYMBOL (local): {} ({:?})", + tcx.symbol_name(Instance::mono(tcx, def_id)), + export_level); + (def_id, export_level) + }) + .collect(); + + if let Some(id) = *tcx.sess.derive_registrar_fn.get() { + let def_id = tcx.hir.local_def_id(id); + reachable_non_generics.insert(def_id, SymbolExportLevel::C); + } + + if let Some(id) = *tcx.sess.plugin_registrar_fn.get() { + let def_id = tcx.hir.local_def_id(id); + reachable_non_generics.insert(def_id, SymbolExportLevel::C); + } + + Lrc::new(reachable_non_generics) +} + +fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool { + let export_threshold = threshold(tcx); + + if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) { + level.is_below_threshold(export_threshold) + } else { + false + } +} + +fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> bool { + tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) +} + +fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Arc, + SymbolExportLevel)>> +{ + assert_eq!(cnum, LOCAL_CRATE); + + if !tcx.sess.opts.output_types.should_codegen() { + return Arc::new(vec![]) + } + + let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE) + .iter() + .map(|(&def_id, &level)| { + (ExportedSymbol::NonGeneric(def_id), level) + }) + .collect(); + + if let Some(_) = *tcx.sess.entry_fn.borrow() { + let symbol_name = "main".to_string(); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::C)); + } + + if tcx.sess.allocator_kind.get().is_some() { + for method in ALLOCATOR_METHODS { + let symbol_name = format!("__rust_{}", method.name); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::Rust)); + } + } + + if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { + // These are weak symbols that point to the profile version and the + // profile name, which need to be treated as exported so LTO doesn't nix + // them. + const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [ + "__llvm_profile_raw_version", + "__llvm_profile_filename", + ]; + for sym in &PROFILER_WEAK_SYMBOLS { + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym)); + symbols.push((exported_symbol, SymbolExportLevel::C)); + } + } + + if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) { + let symbol_name = metadata_symbol_name(tcx); + let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); + + symbols.push((exported_symbol, SymbolExportLevel::Rust)); + } + + if tcx.share_generics() && tcx.local_crate_exports_generics() { + use rustc::mir::mono::{Linkage, Visibility, MonoItem}; + use rustc::ty::InstanceDef; + + // Normally, we require that shared monomorphizations are not hidden, + // because if we want to re-use a monomorphization from a Rust dylib, it + // needs to be exported. + // However, on platforms that don't allow for Rust dylibs, having + // external linkage is enough for monomorphization to be linked to. + let need_visibility = tcx.sess.target.target.options.dynamic_linking && + !tcx.sess.target.target.options.only_cdylib; + + let (_, cgus) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + + for (mono_item, &(linkage, visibility)) in cgus.iter() + .flat_map(|cgu| cgu.items().iter()) { + if linkage != Linkage::External { + // We can only re-use things with external linkage, otherwise + // we'll get a linker error + continue + } + + if need_visibility && visibility == Visibility::Hidden { + // If we potentially share things from Rust dylibs, they must + // not be hidden + continue + } + + if let &MonoItem::Fn(Instance { + def: InstanceDef::Item(def_id), + substs, + }) = mono_item { + if substs.types().next().is_some() { + symbols.push((ExportedSymbol::Generic(def_id, substs), + SymbolExportLevel::Rust)); + } + } + } + } + + // Sort so we get a stable incr. comp. hash. + symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| { + symbol1.compare_stable(tcx, symbol2) + }); + + Arc::new(symbols) +} + +fn upstream_monomorphizations_provider<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum) + -> Lrc, CrateNum>>>> +{ + debug_assert!(cnum == LOCAL_CRATE); + + let cnums = tcx.all_crate_nums(LOCAL_CRATE); + + let mut instances = DefIdMap(); + + let cnum_stable_ids: IndexVec = { + let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, + cnums.len() + 1); + + for &cnum in cnums.iter() { + cnum_stable_ids[cnum] = tcx.def_path_hash(DefId { + krate: cnum, + index: CRATE_DEF_INDEX, + }).0; + } + + cnum_stable_ids + }; + + for &cnum in cnums.iter() { + for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { + if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { + let substs_map = instances.entry(def_id) + .or_insert_with(|| FxHashMap()); + + match substs_map.entry(substs) { + Occupied(mut e) => { + // If there are multiple monomorphizations available, + // we select one deterministically. + let other_cnum = *e.get(); + if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] { + e.insert(cnum); + } + } + Vacant(e) => { + e.insert(cnum); + } + } + } + } + } + + Lrc::new(instances.into_iter() + .map(|(key, value)| (key, Lrc::new(value))) + .collect()) +} + +fn upstream_monomorphizations_for_provider<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + def_id: DefId) + -> Option, CrateNum>>> +{ + debug_assert!(!def_id.is_local()); + tcx.upstream_monomorphizations(LOCAL_CRATE) + .get(&def_id) + .cloned() +} + +fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool { + if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { + !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id) + } else { + bug!("is_unreachable_local_definition called with non-local DefId: {:?}", + def_id) + } +} + +pub fn provide(providers: &mut Providers) { + providers.reachable_non_generics = reachable_non_generics_provider; + providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; + providers.exported_symbols = exported_symbols_provider_local; + providers.upstream_monomorphizations = upstream_monomorphizations_provider; + providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; +} + +pub fn provide_extern(providers: &mut Providers) { + providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; + providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; +} + +fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { + // We export anything that's not mangled at the "C" layer as it probably has + // to do with ABI concerns. We do not, however, apply such treatment to + // special symbols in the standard library for various plumbing between + // core/std/allocators/etc. For example symbols used to hook up allocation + // are not considered for export + let codegen_fn_attrs = tcx.codegen_fn_attrs(sym_def_id); + let is_extern = codegen_fn_attrs.contains_extern_indicator(); + let std_internal = + codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); + + if is_extern && !std_internal { + SymbolExportLevel::C + } else { + SymbolExportLevel::Rust + } +} diff --git a/src/librustc_codegen_llvm/back/wasm.rs b/src/librustc_codegen_llvm/back/wasm.rs new file mode 100644 index 00000000000..d6d386c9fbe --- /dev/null +++ b/src/librustc_codegen_llvm/back/wasm.rs @@ -0,0 +1,261 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::BTreeMap; +use std::fs; +use std::path::Path; +use std::str; + +use rustc_data_structures::fx::FxHashMap; +use serialize::leb128; + +// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec +const WASM_IMPORT_SECTION_ID: u8 = 2; + +const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; +const WASM_EXTERNAL_KIND_TABLE: u8 = 1; +const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; +const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; + +/// Append all the custom sections listed in `sections` to the wasm binary +/// specified at `path`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to create custom +/// sections in wasm files nor does LLD have the ability to merge these sections +/// into one larger section when linking. It's expected that this will +/// eventually get implemented, however! +/// +/// Until that time though this is a custom implementation in rustc to append +/// all sections to a wasm file to the finished product that LLD produces. +/// +/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097, +/// although after that support will need to be in LLD as well. +pub fn add_custom_sections(path: &Path, sections: &BTreeMap>) { + if sections.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + + // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section + let mut wasm = WasmEncoder { data: wasm }; + for (section, bytes) in sections { + // write the `id` identifier, 0 for a custom section + wasm.byte(0); + + // figure out how long our name descriptor will be + let mut name = WasmEncoder::new(); + name.str(section); + + // write the length of the payload followed by all its contents + wasm.u32((bytes.len() + name.data.len()) as u32); + wasm.data.extend_from_slice(&name.data); + wasm.data.extend_from_slice(bytes); + } + + fs::write(path, &wasm.data).expect("failed to write wasm output"); +} + +/// Rewrite the module imports are listed from in a wasm module given the field +/// name to module name mapping in `import_map`. +/// +/// LLVM 6 which we're using right now doesn't have the ability to configure the +/// module a wasm symbol is import from. Rather all imported symbols come from +/// the bland `"env"` module unconditionally. Furthermore we'd *also* need +/// support in LLD for preserving these import modules, which it unfortunately +/// currently does not. +/// +/// This function is intended as a hack for now where we manually rewrite the +/// wasm output by LLVM to have the correct import modules listed. The +/// `#[wasm_import_module]` attribute in Rust translates to the module that each +/// symbol is imported from, so here we manually go through the wasm file, +/// decode it, rewrite imports, and then rewrite the wasm module. +/// +/// Support for this was added to LLVM in +/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still +/// needs to be added (AFAIK at the time of this writing) to LLD +pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { + if import_map.len() == 0 { + return + } + + let wasm = fs::read(path).expect("failed to read wasm output"); + let mut ret = WasmEncoder::new(); + ret.data.extend(&wasm[..8]); + + // skip the 8 byte wasm/version header + for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { + ret.byte(id); + if id == WASM_IMPORT_SECTION_ID { + info!("rewriting import section"); + let data = rewrite_import_section( + &mut WasmDecoder::new(raw), + import_map, + ); + ret.bytes(&data); + } else { + info!("carry forward section {}, {} bytes long", id, raw.len()); + ret.bytes(raw); + } + } + + fs::write(path, &ret.data).expect("failed to write wasm output"); + + fn rewrite_import_section( + wasm: &mut WasmDecoder, + import_map: &FxHashMap, + ) + -> Vec + { + let mut dst = WasmEncoder::new(); + let n = wasm.u32(); + dst.u32(n); + info!("rewriting {} imports", n); + for _ in 0..n { + rewrite_import_entry(wasm, &mut dst, import_map); + } + return dst.data + } + + fn rewrite_import_entry(wasm: &mut WasmDecoder, + dst: &mut WasmEncoder, + import_map: &FxHashMap) { + // More info about the binary format here is available at: + // https://webassembly.github.io/spec/core/binary/modules.html#import-section + // + // Note that you can also find the whole point of existence of this + // function here, where we map the `module` name to a different one if + // we've got one listed. + let module = wasm.str(); + let field = wasm.str(); + let new_module = if module == "env" { + import_map.get(field).map(|s| &**s).unwrap_or(module) + } else { + module + }; + info!("import rewrite ({} => {}) / {}", module, new_module, field); + dst.str(new_module); + dst.str(field); + let kind = wasm.byte(); + dst.byte(kind); + match kind { + WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), + WASM_EXTERNAL_KIND_TABLE => { + dst.byte(wasm.byte()); // element_type + dst.limits(wasm.limits()); + } + WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), + WASM_EXTERNAL_KIND_GLOBAL => { + dst.byte(wasm.byte()); // content_type + dst.bool(wasm.bool()); // mutable + } + b => panic!("unknown kind: {}", b), + } + } +} + +struct WasmSections<'a>(WasmDecoder<'a>); + +impl<'a> Iterator for WasmSections<'a> { + type Item = (u8, &'a [u8]); + + fn next(&mut self) -> Option<(u8, &'a [u8])> { + if self.0.data.len() == 0 { + return None + } + + // see https://webassembly.github.io/spec/core/binary/modules.html#sections + let id = self.0.byte(); + let section_len = self.0.u32(); + info!("new section {} / {} bytes", id, section_len); + let section = self.0.skip(section_len as usize); + Some((id, section)) + } +} + +struct WasmDecoder<'a> { + data: &'a [u8], +} + +impl<'a> WasmDecoder<'a> { + fn new(data: &'a [u8]) -> WasmDecoder<'a> { + WasmDecoder { data } + } + + fn byte(&mut self) -> u8 { + self.skip(1)[0] + } + + fn u32(&mut self) -> u32 { + let (n, l1) = leb128::read_u32_leb128(self.data); + self.data = &self.data[l1..]; + return n + } + + fn skip(&mut self, amt: usize) -> &'a [u8] { + let (data, rest) = self.data.split_at(amt); + self.data = rest; + data + } + + fn str(&mut self) -> &'a str { + let len = self.u32(); + str::from_utf8(self.skip(len as usize)).unwrap() + } + + fn bool(&mut self) -> bool { + self.byte() == 1 + } + + fn limits(&mut self) -> (u32, Option) { + let has_max = self.bool(); + (self.u32(), if has_max { Some(self.u32()) } else { None }) + } +} + +struct WasmEncoder { + data: Vec, +} + +impl WasmEncoder { + fn new() -> WasmEncoder { + WasmEncoder { data: Vec::new() } + } + + fn u32(&mut self, val: u32) { + let at = self.data.len(); + leb128::write_u32_leb128(&mut self.data, at, val); + } + + fn byte(&mut self, val: u8) { + self.data.push(val); + } + + fn bytes(&mut self, val: &[u8]) { + self.u32(val.len() as u32); + self.data.extend_from_slice(val); + } + + fn str(&mut self, val: &str) { + self.bytes(val.as_bytes()) + } + + fn bool(&mut self, b: bool) { + self.byte(b as u8); + } + + fn limits(&mut self, limits: (u32, Option)) { + self.bool(limits.1.is_some()); + self.u32(limits.0); + if let Some(c) = limits.1 { + self.u32(c); + } + } +} diff --git a/src/librustc_codegen_llvm/back/write.rs b/src/librustc_codegen_llvm/back/write.rs new file mode 100644 index 00000000000..1151e013131 --- /dev/null +++ b/src/librustc_codegen_llvm/back/write.rs @@ -0,0 +1,2390 @@ +// Copyright 2013-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use attributes; +use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; +use back::lto::{self, ModuleBuffer, ThinBuffer}; +use back::link::{self, get_linker, remove}; +use back::command::Command; +use back::linker::LinkerInfo; +use back::symbol_export::ExportedSymbols; +use base; +use consts; +use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir}; +use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; +use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; +use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses, + AllPasses, Sanitizer, Lto}; +use rustc::session::Session; +use rustc::util::nodemap::FxHashMap; +use time_graph::{self, TimeGraph, Timeline}; +use llvm; +use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; +use llvm::{SMDiagnosticRef, ContextRef}; +use {CodegenResults, ModuleSource, ModuleCodegen, CompiledModule, ModuleKind}; +use CrateInfo; +use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; +use rustc::ty::TyCtxt; +use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passes_entry}; +use rustc::util::common::path2cstr; +use rustc::util::fs::{link_or_copy}; +use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; +use errors::emitter::{Emitter}; +use syntax::attr; +use syntax::ext::hygiene::Mark; +use syntax_pos::MultiSpan; +use syntax_pos::symbol::Symbol; +use type_::Type; +use context::{is_pie_binary, get_reloc_model}; +use common::{C_bytes_in_context, val_ty}; +use jobserver::{Client, Acquired}; +use rustc_demangle; + +use std::any::Any; +use std::ffi::{CString, CStr}; +use std::fs; +use std::io::{self, Write}; +use std::mem; +use std::path::{Path, PathBuf}; +use std::str; +use std::sync::Arc; +use std::sync::mpsc::{channel, Sender, Receiver}; +use std::slice; +use std::time::Instant; +use std::thread; +use libc::{c_uint, c_void, c_char, size_t}; + +pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ + ("pic", llvm::RelocMode::PIC), + ("static", llvm::RelocMode::Static), + ("default", llvm::RelocMode::Default), + ("dynamic-no-pic", llvm::RelocMode::DynamicNoPic), + ("ropi", llvm::RelocMode::ROPI), + ("rwpi", llvm::RelocMode::RWPI), + ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI), +]; + +pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ + ("small", llvm::CodeModel::Small), + ("kernel", llvm::CodeModel::Kernel), + ("medium", llvm::CodeModel::Medium), + ("large", llvm::CodeModel::Large), +]; + +pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ + ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), + ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), + ("initial-exec", llvm::ThreadLocalMode::InitialExec), + ("local-exec", llvm::ThreadLocalMode::LocalExec), +]; + +pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { + match llvm::last_error() { + Some(err) => handler.fatal(&format!("{}: {}", msg, err)), + None => handler.fatal(&msg), + } +} + +pub fn write_output_file( + handler: &errors::Handler, + target: llvm::TargetMachineRef, + pm: llvm::PassManagerRef, + m: ModuleRef, + output: &Path, + file_type: llvm::FileType) -> Result<(), FatalError> { + unsafe { + let output_c = path2cstr(output); + let result = llvm::LLVMRustWriteOutputFile( + target, pm, m, output_c.as_ptr(), file_type); + if result.into_result().is_err() { + let msg = format!("could not write output to {}", output.display()); + Err(llvm_err(handler, msg)) + } else { + Ok(()) + } + } +} + +fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { + match optimize { + config::OptLevel::No => llvm::CodeGenOptLevel::None, + config::OptLevel::Less => llvm::CodeGenOptLevel::Less, + config::OptLevel::Default => llvm::CodeGenOptLevel::Default, + config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive, + _ => llvm::CodeGenOptLevel::Default, + } +} + +fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { + match optimize { + config::OptLevel::Size => llvm::CodeGenOptSizeDefault, + config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive, + _ => llvm::CodeGenOptSizeNone, + } +} + +pub fn create_target_machine(sess: &Session, find_features: bool) -> TargetMachineRef { + target_machine_factory(sess, find_features)().unwrap_or_else(|err| { + llvm_err(sess.diagnostic(), err).raise() + }) +} + +// If find_features is true this won't access `sess.crate_types` by assuming +// that `is_pie_binary` is false. When we discover LLVM target features +// `sess.crate_types` is uninitialized so we cannot access it. +pub fn target_machine_factory(sess: &Session, find_features: bool) + -> Arc Result + Send + Sync> +{ + let reloc_model = get_reloc_model(sess); + + let opt_level = get_llvm_opt_level(sess.opts.optimize); + let use_softfp = sess.opts.cg.soft_float; + + let ffunction_sections = sess.target.target.options.function_sections; + let fdata_sections = ffunction_sections; + + let code_model_arg = sess.opts.cg.code_model.as_ref().or( + sess.target.target.options.code_model.as_ref(), + ); + + let code_model = match code_model_arg { + Some(s) => { + match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid code model", + code_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } + } + None => llvm::CodeModel::None, + }; + + let singlethread = sess.target.target.options.singlethread; + + let triple = &sess.target.target.llvm_target; + + let triple = CString::new(triple.as_bytes()).unwrap(); + let cpu = sess.target_cpu(); + let cpu = CString::new(cpu.as_bytes()).unwrap(); + let features = attributes::llvm_target_features(sess) + .collect::>() + .join(","); + let features = CString::new(features).unwrap(); + let is_pie_binary = !find_features && is_pie_binary(sess); + let trap_unreachable = sess.target.target.options.trap_unreachable; + + Arc::new(move || { + let tm = unsafe { + llvm::LLVMRustCreateTargetMachine( + triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), + code_model, + reloc_model, + opt_level, + use_softfp, + is_pie_binary, + ffunction_sections, + fdata_sections, + trap_unreachable, + singlethread, + ) + }; + + if tm.is_null() { + Err(format!("Could not create LLVM TargetMachine for triple: {}", + triple.to_str().unwrap())) + } else { + Ok(tm) + } + }) +} + +/// Module-specific configuration for `optimize_and_codegen`. +pub struct ModuleConfig { + /// Names of additional optimization passes to run. + passes: Vec, + /// Some(level) to optimize at a certain level, or None to run + /// absolutely no optimizations (used for the metadata module). + pub opt_level: Option, + + /// Some(level) to optimize binary size, or None to not affect program size. + opt_size: Option, + + pgo_gen: Option, + pgo_use: String, + + // Flags indicating which outputs to produce. + emit_no_opt_bc: bool, + emit_bc: bool, + emit_bc_compressed: bool, + emit_lto_bc: bool, + emit_ir: bool, + emit_asm: bool, + emit_obj: bool, + // Miscellaneous flags. These are mostly copied from command-line + // options. + no_verify: bool, + no_prepopulate_passes: bool, + no_builtins: bool, + time_passes: bool, + vectorize_loop: bool, + vectorize_slp: bool, + merge_functions: bool, + inline_threshold: Option, + // Instead of creating an object file by doing LLVM codegen, just + // make the object file bitcode. Provides easy compatibility with + // emscripten's ecc compiler, when used as the linker. + obj_is_bitcode: bool, + no_integrated_as: bool, + embed_bitcode: bool, + embed_bitcode_marker: bool, +} + +impl ModuleConfig { + fn new(passes: Vec) -> ModuleConfig { + ModuleConfig { + passes, + opt_level: None, + opt_size: None, + + pgo_gen: None, + pgo_use: String::new(), + + emit_no_opt_bc: false, + emit_bc: false, + emit_bc_compressed: false, + emit_lto_bc: false, + emit_ir: false, + emit_asm: false, + emit_obj: false, + obj_is_bitcode: false, + embed_bitcode: false, + embed_bitcode_marker: false, + no_integrated_as: false, + + no_verify: false, + no_prepopulate_passes: false, + no_builtins: false, + time_passes: false, + vectorize_loop: false, + vectorize_slp: false, + merge_functions: false, + inline_threshold: None + } + } + + fn set_flags(&mut self, sess: &Session, no_builtins: bool) { + self.no_verify = sess.no_verify(); + self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; + self.no_builtins = no_builtins || sess.target.target.options.no_builtins; + self.time_passes = sess.time_passes(); + self.inline_threshold = sess.opts.cg.inline_threshold; + self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; + let embed_bitcode = sess.target.target.options.embed_bitcode || + sess.opts.debugging_opts.embed_bitcode || + sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); + if embed_bitcode { + match sess.opts.optimize { + config::OptLevel::No | + config::OptLevel::Less => { + self.embed_bitcode_marker = embed_bitcode; + } + _ => self.embed_bitcode = embed_bitcode, + } + } + + // Copy what clang does by turning on loop vectorization at O2 and + // slp vectorization at O3. Otherwise configure other optimization aspects + // of this pass manager builder. + // Turn off vectorization for emscripten, as it's not very well supported. + self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && + (sess.opts.optimize == config::OptLevel::Default || + sess.opts.optimize == config::OptLevel::Aggressive) && + !sess.target.target.options.is_like_emscripten; + + self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && + sess.opts.optimize == config::OptLevel::Aggressive && + !sess.target.target.options.is_like_emscripten; + + self.merge_functions = sess.opts.optimize == config::OptLevel::Default || + sess.opts.optimize == config::OptLevel::Aggressive; + } +} + +/// Assembler name and command used by codegen when no_integrated_as is enabled +struct AssemblerCommand { + name: PathBuf, + cmd: Command, +} + +/// Additional resources used by optimize_and_codegen (not module specific) +#[derive(Clone)] +pub struct CodegenContext { + // Resouces needed when running LTO + pub time_passes: bool, + pub lto: Lto, + pub no_landing_pads: bool, + pub save_temps: bool, + pub fewer_names: bool, + pub exported_symbols: Option>, + pub opts: Arc, + pub crate_types: Vec, + pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, + output_filenames: Arc, + regular_module_config: Arc, + metadata_module_config: Arc, + allocator_module_config: Arc, + pub tm_factory: Arc Result + Send + Sync>, + pub msvc_imps_needed: bool, + pub target_pointer_width: String, + debuginfo: config::DebugInfoLevel, + + // Number of cgus excluding the allocator/metadata modules + pub total_cgus: usize, + // Handler to use for diagnostics produced during codegen. + pub diag_emitter: SharedEmitter, + // LLVM passes added by plugins. + pub plugin_passes: Vec, + // LLVM optimizations for which we want to print remarks. + pub remark: Passes, + // Worker thread number + pub worker: usize, + // The incremental compilation session directory, or None if we are not + // compiling incrementally + pub incr_comp_session_dir: Option, + // Channel back to the main control thread to send messages to + coordinator_send: Sender>, + // A reference to the TimeGraph so we can register timings. None means that + // measuring is disabled. + time_graph: Option, + // The assembler command if no_integrated_as option is enabled, None otherwise + assembler_cmd: Option>, +} + +impl CodegenContext { + pub fn create_diag_handler(&self) -> Handler { + Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) + } + + pub(crate) fn config(&self, kind: ModuleKind) -> &ModuleConfig { + match kind { + ModuleKind::Regular => &self.regular_module_config, + ModuleKind::Metadata => &self.metadata_module_config, + ModuleKind::Allocator => &self.allocator_module_config, + } + } + + pub(crate) fn save_temp_bitcode(&self, module: &ModuleCodegen, name: &str) { + if !self.save_temps { + return + } + unsafe { + let ext = format!("{}.bc", name); + let cgu = Some(&module.name[..]); + let path = self.output_filenames.temp_path_ext(&ext, cgu); + let cstr = path2cstr(&path); + let llmod = module.llvm().unwrap().llmod; + llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); + } + } +} + +struct DiagnosticHandlers<'a> { + inner: Box<(&'a CodegenContext, &'a Handler)>, + llcx: ContextRef, +} + +impl<'a> DiagnosticHandlers<'a> { + fn new(cgcx: &'a CodegenContext, + handler: &'a Handler, + llcx: ContextRef) -> DiagnosticHandlers<'a> { + let data = Box::new((cgcx, handler)); + unsafe { + let arg = &*data as &(_, _) as *const _ as *mut _; + llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); + llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); + } + DiagnosticHandlers { + inner: data, + llcx: llcx, + } + } +} + +impl<'a> Drop for DiagnosticHandlers<'a> { + fn drop(&mut self) { + unsafe { + llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); + llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); + } + } +} + +unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, + msg: &'b str, + cookie: c_uint) { + cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); +} + +unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, + user: *const c_void, + cookie: c_uint) { + if user.is_null() { + return + } + let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); + + let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) + .expect("non-UTF8 SMDiagnostic"); + + report_inline_asm(cgcx, &msg, cookie); +} + +unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { + if user.is_null() { + return + } + let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); + + match llvm::diagnostic::Diagnostic::unpack(info) { + llvm::diagnostic::InlineAsm(inline) => { + report_inline_asm(cgcx, + &llvm::twine_to_string(inline.message), + inline.cookie); + } + + llvm::diagnostic::Optimization(opt) => { + let enabled = match cgcx.remark { + AllPasses => true, + SomePasses(ref v) => v.iter().any(|s| *s == opt.pass_name), + }; + + if enabled { + diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", + opt.kind.describe(), + opt.pass_name, + opt.filename, + opt.line, + opt.column, + opt.message)); + } + } + llvm::diagnostic::PGO(diagnostic_ref) => { + let msg = llvm::build_string(|s| { + llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) + }).expect("non-UTF8 PGO diagnostic"); + diag_handler.warn(&msg); + } + llvm::diagnostic::UnknownDiagnostic(..) => {}, + } +} + +// Unsafe due to LLVM calls. +unsafe fn optimize(cgcx: &CodegenContext, + diag_handler: &Handler, + module: &ModuleCodegen, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result<(), FatalError> +{ + let (llmod, llcx, tm) = match module.source { + ModuleSource::Codegened(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("optimize_and_codegen: called with ModuleSource::Preexisting") + } + }; + + let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + + if config.emit_no_opt_bc { + let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); + let out = path2cstr(&out); + llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); + } + + if config.opt_level.is_some() { + // Create the two optimizing pass managers. These mirror what clang + // does, and are by populated by LLVM's default PassManagerBuilder. + // Each manager has a different set of passes, but they also share + // some common passes. + let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); + let mpm = llvm::LLVMCreatePassManager(); + + // If we're verifying or linting, add them to the function pass + // manager. + let addpass = |pass_name: &str| { + let pass_name = CString::new(pass_name).unwrap(); + let pass = llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()); + if pass.is_null() { + return false; + } + let pass_manager = match llvm::LLVMRustPassKind(pass) { + llvm::PassKind::Function => fpm, + llvm::PassKind::Module => mpm, + llvm::PassKind::Other => { + diag_handler.err("Encountered LLVM pass kind we can't handle"); + return true + }, + }; + llvm::LLVMRustAddPass(pass_manager, pass); + true + }; + + if !config.no_verify { assert!(addpass("verify")); } + if !config.no_prepopulate_passes { + llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); + llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); + let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); + let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal; + with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| { + llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); + llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); + }) + } + + for pass in &config.passes { + if !addpass(pass) { + diag_handler.warn(&format!("unknown pass `{}`, ignoring", + pass)); + } + } + + for pass in &cgcx.plugin_passes { + if !addpass(pass) { + diag_handler.err(&format!("a plugin asked for LLVM pass \ + `{}` but LLVM does not \ + recognize it", pass)); + } + } + + diag_handler.abort_if_errors(); + + // Finally, run the actual optimization passes + time_ext(config.time_passes, + None, + &format!("llvm function passes [{}]", module_name.unwrap()), + || { + llvm::LLVMRustRunFunctionPassManager(fpm, llmod) + }); + timeline.record("fpm"); + time_ext(config.time_passes, + None, + &format!("llvm module passes [{}]", module_name.unwrap()), + || { + llvm::LLVMRunPassManager(mpm, llmod) + }); + + // Deallocate managers that we're now done with + llvm::LLVMDisposePassManager(fpm); + llvm::LLVMDisposePassManager(mpm); + } + Ok(()) +} + +fn generate_lto_work(cgcx: &CodegenContext, + modules: Vec) + -> Vec<(WorkItem, u64)> +{ + let mut timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(CODEGEN_WORKER_TIMELINE, + CODEGEN_WORK_PACKAGE_KIND, + "generate lto") + }).unwrap_or(Timeline::noop()); + let lto_modules = lto::run(cgcx, modules, &mut timeline) + .unwrap_or_else(|e| e.raise()); + + lto_modules.into_iter().map(|module| { + let cost = module.cost(); + (WorkItem::LTO(module), cost) + }).collect() +} + +unsafe fn codegen(cgcx: &CodegenContext, + diag_handler: &Handler, + module: ModuleCodegen, + config: &ModuleConfig, + timeline: &mut Timeline) + -> Result +{ + timeline.record("codegen"); + let (llmod, llcx, tm) = match module.source { + ModuleSource::Codegened(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), + ModuleSource::Preexisting(_) => { + bug!("codegen: called with ModuleSource::Preexisting") + } + }; + let module_name = module.name.clone(); + let module_name = Some(&module_name[..]); + let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); + + if cgcx.msvc_imps_needed { + create_msvc_imps(cgcx, llcx, llmod); + } + + // A codegen-specific pass manager is used to generate object + // files for an LLVM module. + // + // Apparently each of these pass managers is a one-shot kind of + // thing, so we create a new one for each type of output. The + // pass manager passed to the closure should be ensured to not + // escape the closure itself, and the manager should only be + // used once. + unsafe fn with_codegen(tm: TargetMachineRef, + llmod: ModuleRef, + no_builtins: bool, + f: F) -> R + where F: FnOnce(PassManagerRef) -> R, + { + let cpm = llvm::LLVMCreatePassManager(); + llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); + llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); + f(cpm) + } + + // If we don't have the integrated assembler, then we need to emit asm + // from LLVM and use `gcc` to create the object file. + let asm_to_obj = config.emit_obj && config.no_integrated_as; + + // Change what we write and cleanup based on whether obj files are + // just llvm bitcode. In that case write bitcode, and possibly + // delete the bitcode if it wasn't requested. Don't generate the + // machine code, instead copy the .o file from the .bc + let write_bc = config.emit_bc || config.obj_is_bitcode; + let rm_bc = !config.emit_bc && config.obj_is_bitcode; + let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; + let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; + + let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); + let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); + + + if write_bc || config.emit_bc_compressed || config.embed_bitcode { + let thin; + let old; + let data = if llvm::LLVMRustThinLTOAvailable() { + thin = ThinBuffer::new(llmod); + thin.data() + } else { + old = ModuleBuffer::new(llmod); + old.data() + }; + timeline.record("make-bc"); + + if write_bc { + if let Err(e) = fs::write(&bc_out, data) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("write-bc"); + } + + if config.embed_bitcode { + embed_bitcode(cgcx, llcx, llmod, Some(data)); + timeline.record("embed-bc"); + } + + if config.emit_bc_compressed { + let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); + let data = bytecode::encode(&module.llmod_id, data); + if let Err(e) = fs::write(&dst, data) { + diag_handler.err(&format!("failed to write bytecode: {}", e)); + } + timeline.record("compress-bc"); + } + } else if config.embed_bitcode_marker { + embed_bitcode(cgcx, llcx, llmod, None); + } + + time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()), + || -> Result<(), FatalError> { + if config.emit_ir { + let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); + let out = path2cstr(&out); + + extern "C" fn demangle_callback(input_ptr: *const c_char, + input_len: size_t, + output_ptr: *mut c_char, + output_len: size_t) -> size_t { + let input = unsafe { + slice::from_raw_parts(input_ptr as *const u8, input_len as usize) + }; + + let input = match str::from_utf8(input) { + Ok(s) => s, + Err(_) => return 0, + }; + + let output = unsafe { + slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) + }; + let mut cursor = io::Cursor::new(output); + + let demangled = match rustc_demangle::try_demangle(input) { + Ok(d) => d, + Err(_) => return 0, + }; + + if let Err(_) = write!(cursor, "{:#}", demangled) { + // Possible only if provided buffer is not big enough + return 0; + } + + cursor.position() as size_t + } + + with_codegen(tm, llmod, config.no_builtins, |cpm| { + llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); + llvm::LLVMDisposePassManager(cpm); + }); + timeline.record("ir"); + } + + if config.emit_asm || asm_to_obj { + let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + + // We can't use the same module for asm and binary output, because that triggers + // various errors like invalid IR or broken binaries, so we might have to clone the + // module to produce the asm output + let llmod = if config.emit_obj { + llvm::LLVMCloneModule(llmod) + } else { + llmod + }; + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(diag_handler, tm, cpm, llmod, &path, + llvm::FileType::AssemblyFile) + })?; + if config.emit_obj { + llvm::LLVMDisposeModule(llmod); + } + timeline.record("asm"); + } + + if write_obj { + with_codegen(tm, llmod, config.no_builtins, |cpm| { + write_output_file(diag_handler, tm, cpm, llmod, &obj_out, + llvm::FileType::ObjectFile) + })?; + timeline.record("obj"); + } else if asm_to_obj { + let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); + run_assembler(cgcx, diag_handler, &assembly, &obj_out); + timeline.record("asm_to_obj"); + + if !config.emit_asm && !cgcx.save_temps { + drop(fs::remove_file(&assembly)); + } + } + + Ok(()) + })?; + + if copy_bc_to_obj { + debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); + if let Err(e) = link_or_copy(&bc_out, &obj_out) { + diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); + } + } + + if rm_bc { + debug!("removing_bitcode {:?}", bc_out); + if let Err(e) = fs::remove_file(&bc_out) { + diag_handler.err(&format!("failed to remove bitcode: {}", e)); + } + } + + drop(handlers); + Ok(module.into_compiled_module(config.emit_obj, + config.emit_bc, + config.emit_bc_compressed, + &cgcx.output_filenames)) +} + +/// Embed the bitcode of an LLVM module in the LLVM module itself. +/// +/// This is done primarily for iOS where it appears to be standard to compile C +/// code at least with `-fembed-bitcode` which creates two sections in the +/// executable: +/// +/// * __LLVM,__bitcode +/// * __LLVM,__cmdline +/// +/// It appears *both* of these sections are necessary to get the linker to +/// recognize what's going on. For us though we just always throw in an empty +/// cmdline section. +/// +/// Furthermore debug/O1 builds don't actually embed bitcode but rather just +/// embed an empty section. +/// +/// Basically all of this is us attempting to follow in the footsteps of clang +/// on iOS. See #35968 for lots more info. +unsafe fn embed_bitcode(cgcx: &CodegenContext, + llcx: ContextRef, + llmod: ModuleRef, + bitcode: Option<&[u8]>) { + let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[])); + let llglobal = llvm::LLVMAddGlobal( + llmod, + val_ty(llconst).to_ref(), + "rustc.embedded.module\0".as_ptr() as *const _, + ); + llvm::LLVMSetInitializer(llglobal, llconst); + + let is_apple = cgcx.opts.target_triple.triple().contains("-ios") || + cgcx.opts.target_triple.triple().contains("-darwin"); + + let section = if is_apple { + "__LLVM,__bitcode\0" + } else { + ".llvmbc\0" + }; + llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); + llvm::LLVMSetGlobalConstant(llglobal, llvm::True); + + let llconst = C_bytes_in_context(llcx, &[]); + let llglobal = llvm::LLVMAddGlobal( + llmod, + val_ty(llconst).to_ref(), + "rustc.embedded.cmdline\0".as_ptr() as *const _, + ); + llvm::LLVMSetInitializer(llglobal, llconst); + let section = if is_apple { + "__LLVM,__cmdline\0" + } else { + ".llvmcmd\0" + }; + llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); + llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); +} + +pub(crate) struct CompiledModules { + pub modules: Vec, + pub metadata_module: CompiledModule, + pub allocator_module: Option, +} + +fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { + sess.crate_types.borrow().contains(&config::CrateTypeRlib) && + sess.opts.output_types.contains_key(&OutputType::Exe) +} + +pub fn start_async_codegen(tcx: TyCtxt, + time_graph: Option, + link: LinkMeta, + metadata: EncodedMetadata, + coordinator_receive: Receiver>, + total_cgus: usize) + -> OngoingCodegen { + let sess = tcx.sess; + let crate_name = tcx.crate_name(LOCAL_CRATE); + let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); + let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, + "windows_subsystem"); + let windows_subsystem = subsystem.map(|subsystem| { + if subsystem != "windows" && subsystem != "console" { + tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ + `windows` and `console` are allowed", + subsystem)); + } + subsystem.to_string() + }); + + let linker_info = LinkerInfo::new(tcx); + let crate_info = CrateInfo::new(tcx); + + // Figure out what we actually need to build. + let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); + let mut metadata_config = ModuleConfig::new(vec![]); + let mut allocator_config = ModuleConfig::new(vec![]); + + if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { + match *sanitizer { + Sanitizer::Address => { + modules_config.passes.push("asan".to_owned()); + modules_config.passes.push("asan-module".to_owned()); + } + Sanitizer::Memory => { + modules_config.passes.push("msan".to_owned()) + } + Sanitizer::Thread => { + modules_config.passes.push("tsan".to_owned()) + } + _ => {} + } + } + + if sess.opts.debugging_opts.profile { + modules_config.passes.push("insert-gcov-profiling".to_owned()) + } + + modules_config.pgo_gen = sess.opts.debugging_opts.pgo_gen.clone(); + modules_config.pgo_use = sess.opts.debugging_opts.pgo_use.clone(); + + modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); + modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize)); + + // Save all versions of the bytecode if we're saving our temporaries. + if sess.opts.cg.save_temps { + modules_config.emit_no_opt_bc = true; + modules_config.emit_bc = true; + modules_config.emit_lto_bc = true; + metadata_config.emit_bc = true; + allocator_config.emit_bc = true; + } + + // Emit compressed bitcode files for the crate if we're emitting an rlib. + // Whenever an rlib is created, the bitcode is inserted into the archive in + // order to allow LTO against it. + if need_crate_bitcode_for_rlib(sess) { + modules_config.emit_bc_compressed = true; + allocator_config.emit_bc_compressed = true; + } + + modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as || + tcx.sess.target.target.options.no_integrated_as; + + for output_type in sess.opts.output_types.keys() { + match *output_type { + OutputType::Bitcode => { modules_config.emit_bc = true; } + OutputType::LlvmAssembly => { modules_config.emit_ir = true; } + OutputType::Assembly => { + modules_config.emit_asm = true; + // If we're not using the LLVM assembler, this function + // could be invoked specially with output_type_assembly, so + // in this case we still want the metadata object file. + if !sess.opts.output_types.contains_key(&OutputType::Assembly) { + metadata_config.emit_obj = true; + allocator_config.emit_obj = true; + } + } + OutputType::Object => { modules_config.emit_obj = true; } + OutputType::Metadata => { metadata_config.emit_obj = true; } + OutputType::Exe => { + modules_config.emit_obj = true; + metadata_config.emit_obj = true; + allocator_config.emit_obj = true; + }, + OutputType::Mir => {} + OutputType::DepInfo => {} + } + } + + modules_config.set_flags(sess, no_builtins); + metadata_config.set_flags(sess, no_builtins); + allocator_config.set_flags(sess, no_builtins); + + // Exclude metadata and allocator modules from time_passes output, since + // they throw off the "LLVM passes" measurement. + metadata_config.time_passes = false; + allocator_config.time_passes = false; + + let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); + let (codegen_worker_send, codegen_worker_receive) = channel(); + + let coordinator_thread = start_executing_work(tcx, + &crate_info, + shared_emitter, + codegen_worker_send, + coordinator_receive, + total_cgus, + sess.jobserver.clone(), + time_graph.clone(), + Arc::new(modules_config), + Arc::new(metadata_config), + Arc::new(allocator_config)); + + OngoingCodegen { + crate_name, + link, + metadata, + windows_subsystem, + linker_info, + crate_info, + + time_graph, + coordinator_send: tcx.tx_to_llvm_workers.lock().clone(), + codegen_worker_receive, + shared_emitter_main, + future: coordinator_thread, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + } +} + +fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( + sess: &Session, + compiled_modules: &CompiledModules +) -> FxHashMap { + let mut work_products = FxHashMap::default(); + + if sess.opts.incremental.is_none() { + return work_products; + } + + for module in compiled_modules.modules.iter() { + let mut files = vec![]; + + if let Some(ref path) = module.object { + files.push((WorkProductFileKind::Object, path.clone())); + } + if let Some(ref path) = module.bytecode { + files.push((WorkProductFileKind::Bytecode, path.clone())); + } + if let Some(ref path) = module.bytecode_compressed { + files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); + } + + if let Some((id, product)) = + copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) { + work_products.insert(id, product); + } + } + + work_products +} + +fn produce_final_output_artifacts(sess: &Session, + compiled_modules: &CompiledModules, + crate_output: &OutputFilenames) { + let mut user_wants_bitcode = false; + let mut user_wants_objects = false; + + // Produce final compile outputs. + let copy_gracefully = |from: &Path, to: &Path| { + if let Err(e) = fs::copy(from, to) { + sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); + } + }; + + let copy_if_one_unit = |output_type: OutputType, + keep_numbered: bool| { + if compiled_modules.modules.len() == 1 { + // 1) Only one codegen unit. In this case it's no difficulty + // to copy `foo.0.x` to `foo.x`. + let module_name = Some(&compiled_modules.modules[0].name[..]); + let path = crate_output.temp_path(output_type, module_name); + copy_gracefully(&path, + &crate_output.path(output_type)); + if !sess.opts.cg.save_temps && !keep_numbered { + // The user just wants `foo.x`, not `foo.#module-name#.x`. + remove(sess, &path); + } + } else { + let ext = crate_output.temp_path(output_type, None) + .extension() + .unwrap() + .to_str() + .unwrap() + .to_owned(); + + if crate_output.outputs.contains_key(&output_type) { + // 2) Multiple codegen units, with `--emit foo=some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring emit path because multiple .{} files \ + were produced", ext)); + } else if crate_output.single_output_file.is_some() { + // 3) Multiple codegen units, with `-o some_name`. We have + // no good solution for this case, so warn the user. + sess.warn(&format!("ignoring -o because multiple .{} files \ + were produced", ext)); + } else { + // 4) Multiple codegen units, but no explicit name. We + // just leave the `foo.0.x` files in place. + // (We don't have to do any work in this case.) + } + } + }; + + // Flag to indicate whether the user explicitly requested bitcode. + // Otherwise, we produced it only as a temporary output, and will need + // to get rid of it. + for output_type in crate_output.outputs.keys() { + match *output_type { + OutputType::Bitcode => { + user_wants_bitcode = true; + // Copy to .bc, but always keep the .0.bc. There is a later + // check to figure out if we should delete .0.bc files, or keep + // them for making an rlib. + copy_if_one_unit(OutputType::Bitcode, true); + } + OutputType::LlvmAssembly => { + copy_if_one_unit(OutputType::LlvmAssembly, false); + } + OutputType::Assembly => { + copy_if_one_unit(OutputType::Assembly, false); + } + OutputType::Object => { + user_wants_objects = true; + copy_if_one_unit(OutputType::Object, true); + } + OutputType::Mir | + OutputType::Metadata | + OutputType::Exe | + OutputType::DepInfo => {} + } + } + + // Clean up unwanted temporary files. + + // We create the following files by default: + // - #crate#.#module-name#.bc + // - #crate#.#module-name#.o + // - #crate#.crate.metadata.bc + // - #crate#.crate.metadata.o + // - #crate#.o (linked from crate.##.o) + // - #crate#.bc (copied from crate.##.bc) + // We may create additional files if requested by the user (through + // `-C save-temps` or `--emit=` flags). + + if !sess.opts.cg.save_temps { + // Remove the temporary .#module-name#.o objects. If the user didn't + // explicitly request bitcode (with --emit=bc), and the bitcode is not + // needed for building an rlib, then we must remove .#module-name#.bc as + // well. + + // Specific rules for keeping .#module-name#.bc: + // - If the user requested bitcode (`user_wants_bitcode`), and + // codegen_units > 1, then keep it. + // - If the user requested bitcode but codegen_units == 1, then we + // can toss .#module-name#.bc because we copied it to .bc earlier. + // - If we're not building an rlib and the user didn't request + // bitcode, then delete .#module-name#.bc. + // If you change how this works, also update back::link::link_rlib, + // where .#module-name#.bc files are (maybe) deleted after making an + // rlib. + let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); + + let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; + + let keep_numbered_objects = needs_crate_object || + (user_wants_objects && sess.codegen_units() > 1); + + for module in compiled_modules.modules.iter() { + if let Some(ref path) = module.object { + if !keep_numbered_objects { + remove(sess, path); + } + } + + if let Some(ref path) = module.bytecode { + if !keep_numbered_bitcode { + remove(sess, path); + } + } + } + + if !user_wants_bitcode { + if let Some(ref path) = compiled_modules.metadata_module.bytecode { + remove(sess, &path); + } + + if let Some(ref allocator_module) = compiled_modules.allocator_module { + if let Some(ref path) = allocator_module.bytecode { + remove(sess, path); + } + } + } + } + + // We leave the following files around by default: + // - #crate#.o + // - #crate#.crate.metadata.o + // - #crate#.bc + // These are used in linking steps and will be cleaned up afterward. +} + +pub(crate) fn dump_incremental_data(codegen_results: &CodegenResults) { + println!("[incremental] Re-using {} out of {} modules", + codegen_results.modules.iter().filter(|m| m.pre_existing).count(), + codegen_results.modules.len()); +} + +enum WorkItem { + Optimize(ModuleCodegen), + LTO(lto::LtoModuleCodegen), +} + +impl WorkItem { + fn kind(&self) -> ModuleKind { + match *self { + WorkItem::Optimize(ref m) => m.kind, + WorkItem::LTO(_) => ModuleKind::Regular, + } + } + + fn name(&self) -> String { + match *self { + WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), + WorkItem::LTO(ref m) => format!("lto: {}", m.name()), + } + } +} + +enum WorkItemResult { + Compiled(CompiledModule), + NeedsLTO(ModuleCodegen), +} + +fn execute_work_item(cgcx: &CodegenContext, + work_item: WorkItem, + timeline: &mut Timeline) + -> Result +{ + let diag_handler = cgcx.create_diag_handler(); + let config = cgcx.config(work_item.kind()); + let module = match work_item { + WorkItem::Optimize(module) => module, + WorkItem::LTO(mut lto) => { + unsafe { + let module = lto.optimize(cgcx, timeline)?; + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + return Ok(WorkItemResult::Compiled(module)) + } + } + }; + let module_name = module.name.clone(); + + let pre_existing = match module.source { + ModuleSource::Codegened(_) => None, + ModuleSource::Preexisting(ref wp) => Some(wp.clone()), + }; + + if let Some(wp) = pre_existing { + let incr_comp_session_dir = cgcx.incr_comp_session_dir + .as_ref() + .unwrap(); + let name = &module.name; + let mut object = None; + let mut bytecode = None; + let mut bytecode_compressed = None; + for (kind, saved_file) in wp.saved_files { + let obj_out = match kind { + WorkProductFileKind::Object => { + let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); + object = Some(path.clone()); + path + } + WorkProductFileKind::Bytecode => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); + bytecode = Some(path.clone()); + path + } + WorkProductFileKind::BytecodeCompressed => { + let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) + .with_extension(RLIB_BYTECODE_EXTENSION); + bytecode_compressed = Some(path.clone()); + path + } + }; + let source_file = in_incr_comp_dir(&incr_comp_session_dir, + &saved_file); + debug!("copying pre-existing module `{}` from {:?} to {}", + module.name, + source_file, + obj_out.display()); + match link_or_copy(&source_file, &obj_out) { + Ok(_) => { } + Err(err) => { + diag_handler.err(&format!("unable to copy {} to {}: {}", + source_file.display(), + obj_out.display(), + err)); + } + } + } + assert_eq!(object.is_some(), config.emit_obj); + assert_eq!(bytecode.is_some(), config.emit_bc); + assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); + + Ok(WorkItemResult::Compiled(CompiledModule { + llmod_id: module.llmod_id.clone(), + name: module_name, + kind: ModuleKind::Regular, + pre_existing: true, + object, + bytecode, + bytecode_compressed, + })) + } else { + debug!("llvm-optimizing {:?}", module_name); + + unsafe { + optimize(cgcx, &diag_handler, &module, config, timeline)?; + + // After we've done the initial round of optimizations we need to + // decide whether to synchronously codegen this module or ship it + // back to the coordinator thread for further LTO processing (which + // has to wait for all the initial modules to be optimized). + // + // Here we dispatch based on the `cgcx.lto` and kind of module we're + // codegenning... + let needs_lto = match cgcx.lto { + Lto::No => false, + + // Here we've got a full crate graph LTO requested. We ignore + // this, however, if the crate type is only an rlib as there's + // no full crate graph to process, that'll happen later. + // + // This use case currently comes up primarily for targets that + // require LTO so the request for LTO is always unconditionally + // passed down to the backend, but we don't actually want to do + // anything about it yet until we've got a final product. + Lto::Yes | Lto::Fat | Lto::Thin => { + cgcx.crate_types.len() != 1 || + cgcx.crate_types[0] != config::CrateTypeRlib + } + + // When we're automatically doing ThinLTO for multi-codegen-unit + // builds we don't actually want to LTO the allocator modules if + // it shows up. This is due to various linker shenanigans that + // we'll encounter later. + // + // Additionally here's where we also factor in the current LLVM + // version. If it doesn't support ThinLTO we skip this. + Lto::ThinLocal => { + module.kind != ModuleKind::Allocator && + llvm::LLVMRustThinLTOAvailable() + } + }; + + // Metadata modules never participate in LTO regardless of the lto + // settings. + let needs_lto = needs_lto && module.kind != ModuleKind::Metadata; + + // Don't run LTO passes when cross-lang LTO is enabled. The linker + // will do that for us in this case. + let needs_lto = needs_lto && + !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); + + if needs_lto { + Ok(WorkItemResult::NeedsLTO(module)) + } else { + let module = codegen(cgcx, &diag_handler, module, config, timeline)?; + Ok(WorkItemResult::Compiled(module)) + } + } + } +} + +enum Message { + Token(io::Result), + NeedsLTO { + result: ModuleCodegen, + worker_id: usize, + }, + Done { + result: Result, + worker_id: usize, + }, + CodegenDone { + llvm_work_item: WorkItem, + cost: u64, + }, + CodegenComplete, + CodegenItem, +} + +struct Diagnostic { + msg: String, + code: Option, + lvl: Level, +} + +#[derive(PartialEq, Clone, Copy, Debug)] +enum MainThreadWorkerState { + Idle, + Codegenning, + LLVMing, +} + +fn start_executing_work(tcx: TyCtxt, + crate_info: &CrateInfo, + shared_emitter: SharedEmitter, + codegen_worker_send: Sender, + coordinator_receive: Receiver>, + total_cgus: usize, + jobserver: Client, + time_graph: Option, + modules_config: Arc, + metadata_config: Arc, + allocator_config: Arc) + -> thread::JoinHandle> { + let coordinator_send = tcx.tx_to_llvm_workers.lock().clone(); + let sess = tcx.sess; + + // Compute the set of symbols we need to retain when doing LTO (if we need to) + let exported_symbols = { + let mut exported_symbols = FxHashMap(); + + let copy_symbols = |cnum| { + let symbols = tcx.exported_symbols(cnum) + .iter() + .map(|&(s, lvl)| (s.symbol_name(tcx).to_string(), lvl)) + .collect(); + Arc::new(symbols) + }; + + match sess.lto() { + Lto::No => None, + Lto::ThinLocal => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + Some(Arc::new(exported_symbols)) + } + Lto::Yes | Lto::Fat | Lto::Thin => { + exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); + for &cnum in tcx.crates().iter() { + exported_symbols.insert(cnum, copy_symbols(cnum)); + } + Some(Arc::new(exported_symbols)) + } + } + }; + + // First up, convert our jobserver into a helper thread so we can use normal + // mpsc channels to manage our messages and such. + // After we've requested tokens then we'll, when we can, + // get tokens on `coordinator_receive` which will + // get managed in the main loop below. + let coordinator_send2 = coordinator_send.clone(); + let helper = jobserver.into_helper_thread(move |token| { + drop(coordinator_send2.send(Box::new(Message::Token(token)))); + }).expect("failed to spawn helper thread"); + + let mut each_linked_rlib_for_lto = Vec::new(); + drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { + if link::ignored_for_lto(sess, crate_info, cnum) { + return + } + each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); + })); + + let assembler_cmd = if modules_config.no_integrated_as { + // HACK: currently we use linker (gcc) as our assembler + let (name, mut cmd) = get_linker(sess); + cmd.args(&sess.target.target.options.asm_args); + Some(Arc::new(AssemblerCommand { + name, + cmd, + })) + } else { + None + }; + + let cgcx = CodegenContext { + crate_types: sess.crate_types.borrow().clone(), + each_linked_rlib_for_lto, + lto: sess.lto(), + no_landing_pads: sess.no_landing_pads(), + fewer_names: sess.fewer_names(), + save_temps: sess.opts.cg.save_temps, + opts: Arc::new(sess.opts.clone()), + time_passes: sess.time_passes(), + exported_symbols, + plugin_passes: sess.plugin_llvm_passes.borrow().clone(), + remark: sess.opts.cg.remark.clone(), + worker: 0, + incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), + coordinator_send, + diag_emitter: shared_emitter.clone(), + time_graph, + output_filenames: tcx.output_filenames(LOCAL_CRATE), + regular_module_config: modules_config, + metadata_module_config: metadata_config, + allocator_module_config: allocator_config, + tm_factory: target_machine_factory(tcx.sess, false), + total_cgus, + msvc_imps_needed: msvc_imps_needed(tcx), + target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), + debuginfo: tcx.sess.opts.debuginfo, + assembler_cmd, + }; + + // This is the "main loop" of parallel work happening for parallel codegen. + // It's here that we manage parallelism, schedule work, and work with + // messages coming from clients. + // + // There are a few environmental pre-conditions that shape how the system + // is set up: + // + // - Error reporting only can happen on the main thread because that's the + // only place where we have access to the compiler `Session`. + // - LLVM work can be done on any thread. + // - Codegen can only happen on the main thread. + // - Each thread doing substantial work most be in possession of a `Token` + // from the `Jobserver`. + // - The compiler process always holds one `Token`. Any additional `Tokens` + // have to be requested from the `Jobserver`. + // + // Error Reporting + // =============== + // The error reporting restriction is handled separately from the rest: We + // set up a `SharedEmitter` the holds an open channel to the main thread. + // When an error occurs on any thread, the shared emitter will send the + // error message to the receiver main thread (`SharedEmitterMain`). The + // main thread will periodically query this error message queue and emit + // any error messages it has received. It might even abort compilation if + // has received a fatal error. In this case we rely on all other threads + // being torn down automatically with the main thread. + // Since the main thread will often be busy doing codegen work, error + // reporting will be somewhat delayed, since the message queue can only be + // checked in between to work packages. + // + // Work Processing Infrastructure + // ============================== + // The work processing infrastructure knows three major actors: + // + // - the coordinator thread, + // - the main thread, and + // - LLVM worker threads + // + // The coordinator thread is running a message loop. It instructs the main + // thread about what work to do when, and it will spawn off LLVM worker + // threads as open LLVM WorkItems become available. + // + // The job of the main thread is to codegen CGUs into LLVM work package + // (since the main thread is the only thread that can do this). The main + // thread will block until it receives a message from the coordinator, upon + // which it will codegen one CGU, send it to the coordinator and block + // again. This way the coordinator can control what the main thread is + // doing. + // + // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is + // available, it will spawn off a new LLVM worker thread and let it process + // that a WorkItem. When a LLVM worker thread is done with its WorkItem, + // it will just shut down, which also frees all resources associated with + // the given LLVM module, and sends a message to the coordinator that the + // has been completed. + // + // Work Scheduling + // =============== + // The scheduler's goal is to minimize the time it takes to complete all + // work there is, however, we also want to keep memory consumption low + // if possible. These two goals are at odds with each other: If memory + // consumption were not an issue, we could just let the main thread produce + // LLVM WorkItems at full speed, assuring maximal utilization of + // Tokens/LLVM worker threads. However, since codegen usual is faster + // than LLVM processing, the queue of LLVM WorkItems would fill up and each + // WorkItem potentially holds on to a substantial amount of memory. + // + // So the actual goal is to always produce just enough LLVM WorkItems as + // not to starve our LLVM worker threads. That means, once we have enough + // WorkItems in our queue, we can block the main thread, so it does not + // produce more until we need them. + // + // Doing LLVM Work on the Main Thread + // ---------------------------------- + // Since the main thread owns the compiler processes implicit `Token`, it is + // wasteful to keep it blocked without doing any work. Therefore, what we do + // in this case is: We spawn off an additional LLVM worker thread that helps + // reduce the queue. The work it is doing corresponds to the implicit + // `Token`. The coordinator will mark the main thread as being busy with + // LLVM work. (The actual work happens on another OS thread but we just care + // about `Tokens`, not actual threads). + // + // When any LLVM worker thread finishes while the main thread is marked as + // "busy with LLVM work", we can do a little switcheroo: We give the Token + // of the just finished thread to the LLVM worker thread that is working on + // behalf of the main thread's implicit Token, thus freeing up the main + // thread again. The coordinator can then again decide what the main thread + // should do. This allows the coordinator to make decisions at more points + // in time. + // + // Striking a Balance between Throughput and Memory Consumption + // ------------------------------------------------------------ + // Since our two goals, (1) use as many Tokens as possible and (2) keep + // memory consumption as low as possible, are in conflict with each other, + // we have to find a trade off between them. Right now, the goal is to keep + // all workers busy, which means that no worker should find the queue empty + // when it is ready to start. + // How do we do achieve this? Good question :) We actually never know how + // many `Tokens` are potentially available so it's hard to say how much to + // fill up the queue before switching the main thread to LLVM work. Also we + // currently don't have a means to estimate how long a running LLVM worker + // will still be busy with it's current WorkItem. However, we know the + // maximal count of available Tokens that makes sense (=the number of CPU + // cores), so we can take a conservative guess. The heuristic we use here + // is implemented in the `queue_full_enough()` function. + // + // Some Background on Jobservers + // ----------------------------- + // It's worth also touching on the management of parallelism here. We don't + // want to just spawn a thread per work item because while that's optimal + // parallelism it may overload a system with too many threads or violate our + // configuration for the maximum amount of cpu to use for this process. To + // manage this we use the `jobserver` crate. + // + // Job servers are an artifact of GNU make and are used to manage + // parallelism between processes. A jobserver is a glorified IPC semaphore + // basically. Whenever we want to run some work we acquire the semaphore, + // and whenever we're done with that work we release the semaphore. In this + // manner we can ensure that the maximum number of parallel workers is + // capped at any one point in time. + // + // LTO and the coordinator thread + // ------------------------------ + // + // The final job the coordinator thread is responsible for is managing LTO + // and how that works. When LTO is requested what we'll to is collect all + // optimized LLVM modules into a local vector on the coordinator. Once all + // modules have been codegened and optimized we hand this to the `lto` + // module for further optimization. The `lto` module will return back a list + // of more modules to work on, which the coordinator will continue to spawn + // work for. + // + // Each LLVM module is automatically sent back to the coordinator for LTO if + // necessary. There's already optimizations in place to avoid sending work + // back to the coordinator if LTO isn't requested. + return thread::spawn(move || { + // We pretend to be within the top-level LLVM time-passes task here: + set_time_depth(1); + + let max_workers = ::num_cpus::get(); + let mut worker_id_counter = 0; + let mut free_worker_ids = Vec::new(); + let mut get_worker_id = |free_worker_ids: &mut Vec| { + if let Some(id) = free_worker_ids.pop() { + id + } else { + let id = worker_id_counter; + worker_id_counter += 1; + id + } + }; + + // This is where we collect codegen units that have gone all the way + // through codegen and LLVM. + let mut compiled_modules = vec![]; + let mut compiled_metadata_module = None; + let mut compiled_allocator_module = None; + let mut needs_lto = Vec::new(); + let mut started_lto = false; + + // This flag tracks whether all items have gone through codegens + let mut codegen_done = false; + + // This is the queue of LLVM work items that still need processing. + let mut work_items = Vec::<(WorkItem, u64)>::new(); + + // This are the Jobserver Tokens we currently hold. Does not include + // the implicit Token the compiler process owns no matter what. + let mut tokens = Vec::new(); + + let mut main_thread_worker_state = MainThreadWorkerState::Idle; + let mut running = 0; + + let mut llvm_start_time = None; + + // Run the message loop while there's still anything that needs message + // processing: + while !codegen_done || + work_items.len() > 0 || + running > 0 || + needs_lto.len() > 0 || + main_thread_worker_state != MainThreadWorkerState::Idle { + + // While there are still CGUs to be codegened, the coordinator has + // to decide how to utilize the compiler processes implicit Token: + // For codegenning more CGU or for running them through LLVM. + if !codegen_done { + if main_thread_worker_state == MainThreadWorkerState::Idle { + if !queue_full_enough(work_items.len(), running, max_workers) { + // The queue is not full enough, codegen more items: + if let Err(_) = codegen_worker_send.send(Message::CodegenItem) { + panic!("Could not send Message::CodegenItem to main thread") + } + main_thread_worker_state = MainThreadWorkerState::Codegenning; + } else { + // The queue is full enough to not let the worker + // threads starve. Use the implicit Token to do some + // LLVM work too. + let (item, _) = work_items.pop() + .expect("queue empty - queue_full_enough() broken?"); + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } + } + } else { + // If we've finished everything related to normal codegen + // then it must be the case that we've got some LTO work to do. + // Perform the serial work here of figuring out what we're + // going to LTO and then push a bunch of work items onto our + // queue to do LTO + if work_items.len() == 0 && + running == 0 && + main_thread_worker_state == MainThreadWorkerState::Idle { + assert!(!started_lto); + assert!(needs_lto.len() > 0); + started_lto = true; + let modules = mem::replace(&mut needs_lto, Vec::new()); + for (work, cost) in generate_lto_work(&cgcx, modules) { + let insertion_index = work_items + .binary_search_by_key(&cost, |&(_, cost)| cost) + .unwrap_or_else(|e| e); + work_items.insert(insertion_index, (work, cost)); + helper.request_token(); + } + } + + // In this branch, we know that everything has been codegened, + // so it's just a matter of determining whether the implicit + // Token is free to use for LLVM work. + match main_thread_worker_state { + MainThreadWorkerState::Idle => { + if let Some((item, _)) = work_items.pop() { + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + main_thread_worker_state = MainThreadWorkerState::LLVMing; + spawn_work(cgcx, item); + } else { + // There is no unstarted work, so let the main thread + // take over for a running worker. Otherwise the + // implicit token would just go to waste. + // We reduce the `running` counter by one. The + // `tokens.truncate()` below will take care of + // giving the Token back. + debug_assert!(running > 0); + running -= 1; + main_thread_worker_state = MainThreadWorkerState::LLVMing; + } + } + MainThreadWorkerState::Codegenning => { + bug!("codegen worker should not be codegenning after \ + codegen was already completed") + } + MainThreadWorkerState::LLVMing => { + // Already making good use of that token + } + } + } + + // Spin up what work we can, only doing this while we've got available + // parallelism slots and work left to spawn. + while work_items.len() > 0 && running < tokens.len() { + let (item, _) = work_items.pop().unwrap(); + + maybe_start_llvm_timer(cgcx.config(item.kind()), + &mut llvm_start_time); + + let cgcx = CodegenContext { + worker: get_worker_id(&mut free_worker_ids), + .. cgcx.clone() + }; + + spawn_work(cgcx, item); + running += 1; + } + + // Relinquish accidentally acquired extra tokens + tokens.truncate(running); + + let msg = coordinator_receive.recv().unwrap(); + match *msg.downcast::().ok().unwrap() { + // Save the token locally and the next turn of the loop will use + // this to spawn a new unit of work, or it may get dropped + // immediately if we have no more work to spawn. + Message::Token(token) => { + match token { + Ok(token) => { + tokens.push(token); + + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + // If the main thread token is used for LLVM work + // at the moment, we turn that thread into a regular + // LLVM worker thread, so the main thread is free + // to react to codegen demand. + main_thread_worker_state = MainThreadWorkerState::Idle; + running += 1; + } + } + Err(e) => { + let msg = &format!("failed to acquire jobserver token: {}", e); + shared_emitter.fatal(msg); + // Exit the coordinator thread + panic!("{}", msg) + } + } + } + + Message::CodegenDone { llvm_work_item, cost } => { + // We keep the queue sorted by estimated processing cost, + // so that more expensive items are processed earlier. This + // is good for throughput as it gives the main thread more + // time to fill up the queue and it avoids scheduling + // expensive items to the end. + // Note, however, that this is not ideal for memory + // consumption, as LLVM module sizes are not evenly + // distributed. + let insertion_index = + work_items.binary_search_by_key(&cost, |&(_, cost)| cost); + let insertion_index = match insertion_index { + Ok(idx) | Err(idx) => idx + }; + work_items.insert(insertion_index, (llvm_work_item, cost)); + + helper.request_token(); + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + Message::CodegenComplete => { + codegen_done = true; + assert_eq!(main_thread_worker_state, + MainThreadWorkerState::Codegenning); + main_thread_worker_state = MainThreadWorkerState::Idle; + } + + // If a thread exits successfully then we drop a token associated + // with that worker and update our `running` count. We may later + // re-acquire a token to continue running more work. We may also not + // actually drop a token here if the worker was running with an + // "ephemeral token" + // + // Note that if the thread failed that means it panicked, so we + // abort immediately. + Message::Done { result: Ok(compiled_module), worker_id } => { + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + + match compiled_module.kind { + ModuleKind::Regular => { + compiled_modules.push(compiled_module); + } + ModuleKind::Metadata => { + assert!(compiled_metadata_module.is_none()); + compiled_metadata_module = Some(compiled_module); + } + ModuleKind::Allocator => { + assert!(compiled_allocator_module.is_none()); + compiled_allocator_module = Some(compiled_module); + } + } + } + Message::NeedsLTO { result, worker_id } => { + assert!(!started_lto); + if main_thread_worker_state == MainThreadWorkerState::LLVMing { + main_thread_worker_state = MainThreadWorkerState::Idle; + } else { + running -= 1; + } + + free_worker_ids.push(worker_id); + needs_lto.push(result); + } + Message::Done { result: Err(()), worker_id: _ } => { + shared_emitter.fatal("aborting due to worker thread failure"); + // Exit the coordinator thread + return Err(()) + } + Message::CodegenItem => { + bug!("the coordinator should not receive codegen requests") + } + } + } + + if let Some(llvm_start_time) = llvm_start_time { + let total_llvm_time = Instant::now().duration_since(llvm_start_time); + // This is the top-level timing for all of LLVM, set the time-depth + // to zero. + set_time_depth(0); + print_time_passes_entry(cgcx.time_passes, + "LLVM passes", + total_llvm_time); + } + + // Regardless of what order these modules completed in, report them to + // the backend in the same order every time to ensure that we're handing + // out deterministic results. + compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); + + let compiled_metadata_module = compiled_metadata_module + .expect("Metadata module not compiled?"); + + Ok(CompiledModules { + modules: compiled_modules, + metadata_module: compiled_metadata_module, + allocator_module: compiled_allocator_module, + }) + }); + + // A heuristic that determines if we have enough LLVM WorkItems in the + // queue so that the main thread can do LLVM work instead of codegen + fn queue_full_enough(items_in_queue: usize, + workers_running: usize, + max_workers: usize) -> bool { + // Tune me, plz. + items_in_queue > 0 && + items_in_queue >= max_workers.saturating_sub(workers_running / 2) + } + + fn maybe_start_llvm_timer(config: &ModuleConfig, + llvm_start_time: &mut Option) { + // We keep track of the -Ztime-passes output manually, + // since the closure-based interface does not fit well here. + if config.time_passes { + if llvm_start_time.is_none() { + *llvm_start_time = Some(Instant::now()); + } + } + } +} + +pub const CODEGEN_WORKER_ID: usize = ::std::usize::MAX; +pub const CODEGEN_WORKER_TIMELINE: time_graph::TimelineId = + time_graph::TimelineId(CODEGEN_WORKER_ID); +pub const CODEGEN_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#DE9597", "#FED1D3", "#FDC5C7", "#B46668", "#88494B"]); +const LLVM_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = + time_graph::WorkPackageKind(&["#7DB67A", "#C6EEC4", "#ACDAAA", "#579354", "#3E6F3C"]); + +fn spawn_work(cgcx: CodegenContext, work: WorkItem) { + let depth = time_depth(); + + thread::spawn(move || { + set_time_depth(depth); + + // Set up a destructor which will fire off a message that we're done as + // we exit. + struct Bomb { + coordinator_send: Sender>, + result: Option, + worker_id: usize, + } + impl Drop for Bomb { + fn drop(&mut self) { + let worker_id = self.worker_id; + let msg = match self.result.take() { + Some(WorkItemResult::Compiled(m)) => { + Message::Done { result: Ok(m), worker_id } + } + Some(WorkItemResult::NeedsLTO(m)) => { + Message::NeedsLTO { result: m, worker_id } + } + None => Message::Done { result: Err(()), worker_id } + }; + drop(self.coordinator_send.send(Box::new(msg))); + } + } + + let mut bomb = Bomb { + coordinator_send: cgcx.coordinator_send.clone(), + result: None, + worker_id: cgcx.worker, + }; + + // Execute the work itself, and if it finishes successfully then flag + // ourselves as a success as well. + // + // Note that we ignore any `FatalError` coming out of `execute_work_item`, + // as a diagnostic was already sent off to the main thread - just + // surface that there was an error in this worker. + bomb.result = { + let timeline = cgcx.time_graph.as_ref().map(|tg| { + tg.start(time_graph::TimelineId(cgcx.worker), + LLVM_WORK_PACKAGE_KIND, + &work.name()) + }); + let mut timeline = timeline.unwrap_or(Timeline::noop()); + execute_work_item(&cgcx, work, &mut timeline).ok() + }; + }); +} + +pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) { + let assembler = cgcx.assembler_cmd + .as_ref() + .expect("cgcx.assembler_cmd is missing?"); + + let pname = &assembler.name; + let mut cmd = assembler.cmd.clone(); + cmd.arg("-c").arg("-o").arg(object).arg(assembly); + debug!("{:?}", cmd); + + match cmd.output() { + Ok(prog) => { + if !prog.status.success() { + let mut note = prog.stderr.clone(); + note.extend_from_slice(&prog.stdout); + + handler.struct_err(&format!("linking with `{}` failed: {}", + pname.display(), + prog.status)) + .note(&format!("{:?}", &cmd)) + .note(str::from_utf8(¬e[..]).unwrap()) + .emit(); + handler.abort_if_errors(); + } + }, + Err(e) => { + handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); + handler.abort_if_errors(); + } + } +} + +pub unsafe fn with_llvm_pmb(llmod: ModuleRef, + config: &ModuleConfig, + opt_level: llvm::CodeGenOptLevel, + prepare_for_thin_lto: bool, + f: &mut FnMut(llvm::PassManagerBuilderRef)) { + use std::ptr; + + // Create the PassManagerBuilder for LLVM. We configure it with + // reasonable defaults and prepare it to actually populate the pass + // manager. + let builder = llvm::LLVMPassManagerBuilderCreate(); + let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); + let inline_threshold = config.inline_threshold; + + let pgo_gen_path = config.pgo_gen.as_ref().map(|s| { + let s = if s.is_empty() { "default_%m.profraw" } else { s }; + CString::new(s.as_bytes()).unwrap() + }); + + let pgo_use_path = if config.pgo_use.is_empty() { + None + } else { + Some(CString::new(config.pgo_use.as_bytes()).unwrap()) + }; + + llvm::LLVMRustConfigurePassManagerBuilder( + builder, + opt_level, + config.merge_functions, + config.vectorize_slp, + config.vectorize_loop, + prepare_for_thin_lto, + pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), + ); + + llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); + + if opt_size != llvm::CodeGenOptSizeNone { + llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); + } + + llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); + + // Here we match what clang does (kinda). For O0 we only inline + // always-inline functions (but don't add lifetime intrinsics), at O1 we + // inline with lifetime intrinsics, and O2+ we add an inliner with a + // thresholds copied from clang. + match (opt_level, opt_size, inline_threshold) { + (.., Some(t)) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); + } + (llvm::CodeGenOptLevel::Aggressive, ..) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); + } + (_, llvm::CodeGenOptSizeDefault, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); + } + (_, llvm::CodeGenOptSizeAggressive, _) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); + } + (llvm::CodeGenOptLevel::None, ..) => { + llvm::LLVMRustAddAlwaysInlinePass(builder, false); + } + (llvm::CodeGenOptLevel::Less, ..) => { + llvm::LLVMRustAddAlwaysInlinePass(builder, true); + } + (llvm::CodeGenOptLevel::Default, ..) => { + llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); + } + (llvm::CodeGenOptLevel::Other, ..) => { + bug!("CodeGenOptLevel::Other selected") + } + } + + f(builder); + llvm::LLVMPassManagerBuilderDispose(builder); +} + + +enum SharedEmitterMessage { + Diagnostic(Diagnostic), + InlineAsmError(u32, String), + AbortIfErrors, + Fatal(String), +} + +#[derive(Clone)] +pub struct SharedEmitter { + sender: Sender, +} + +pub struct SharedEmitterMain { + receiver: Receiver, +} + +impl SharedEmitter { + pub fn new() -> (SharedEmitter, SharedEmitterMain) { + let (sender, receiver) = channel(); + + (SharedEmitter { sender }, SharedEmitterMain { receiver }) + } + + fn inline_asm_error(&self, cookie: u32, msg: String) { + drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); + } + + fn fatal(&self, msg: &str) { + drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); + } +} + +impl Emitter for SharedEmitter { + fn emit(&mut self, db: &DiagnosticBuilder) { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: db.message(), + code: db.code.clone(), + lvl: db.level, + }))); + for child in &db.children { + drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { + msg: child.message(), + code: None, + lvl: child.level, + }))); + } + drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); + } +} + +impl SharedEmitterMain { + pub fn check(&self, sess: &Session, blocking: bool) { + loop { + let message = if blocking { + match self.receiver.recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + } else { + match self.receiver.try_recv() { + Ok(message) => Ok(message), + Err(_) => Err(()), + } + }; + + match message { + Ok(SharedEmitterMessage::Diagnostic(diag)) => { + let handler = sess.diagnostic(); + match diag.code { + Some(ref code) => { + handler.emit_with_code(&MultiSpan::new(), + &diag.msg, + code.clone(), + diag.lvl); + } + None => { + handler.emit(&MultiSpan::new(), + &diag.msg, + diag.lvl); + } + } + } + Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { + match Mark::from_u32(cookie).expn_info() { + Some(ei) => sess.span_err(ei.call_site, &msg), + None => sess.err(&msg), + } + } + Ok(SharedEmitterMessage::AbortIfErrors) => { + sess.abort_if_errors(); + } + Ok(SharedEmitterMessage::Fatal(msg)) => { + sess.fatal(&msg); + } + Err(_) => { + break; + } + } + + } + } +} + +pub struct OngoingCodegen { + crate_name: Symbol, + link: LinkMeta, + metadata: EncodedMetadata, + windows_subsystem: Option, + linker_info: LinkerInfo, + crate_info: CrateInfo, + time_graph: Option, + coordinator_send: Sender>, + codegen_worker_receive: Receiver, + shared_emitter_main: SharedEmitterMain, + future: thread::JoinHandle>, + output_filenames: Arc, +} + +impl OngoingCodegen { + pub(crate) fn join( + self, + sess: &Session + ) -> (CodegenResults, FxHashMap) { + self.shared_emitter_main.check(sess, true); + let compiled_modules = match self.future.join() { + Ok(Ok(compiled_modules)) => compiled_modules, + Ok(Err(())) => { + sess.abort_if_errors(); + panic!("expected abort due to worker thread errors") + }, + Err(_) => { + sess.fatal("Error during codegen/LLVM phase."); + } + }; + + sess.abort_if_errors(); + + if let Some(time_graph) = self.time_graph { + time_graph.dump(&format!("{}-timings", self.crate_name)); + } + + let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, + &compiled_modules); + + produce_final_output_artifacts(sess, + &compiled_modules, + &self.output_filenames); + + // FIXME: time_llvm_passes support - does this use a global context or + // something? + if sess.codegen_units() == 1 && sess.time_llvm_passes() { + unsafe { llvm::LLVMRustPrintPassTimings(); } + } + + (CodegenResults { + crate_name: self.crate_name, + link: self.link, + metadata: self.metadata, + windows_subsystem: self.windows_subsystem, + linker_info: self.linker_info, + crate_info: self.crate_info, + + modules: compiled_modules.modules, + allocator_module: compiled_modules.allocator_module, + metadata_module: compiled_modules.metadata_module, + }, work_products) + } + + pub(crate) fn submit_pre_codegened_module_to_llvm(&self, + tcx: TyCtxt, + module: ModuleCodegen) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + + // These are generally cheap and won't through off scheduling. + let cost = 0; + submit_codegened_module_to_llvm(tcx, module, cost); + } + + pub fn codegen_finished(&self, tcx: TyCtxt) { + self.wait_for_signal_to_codegen_item(); + self.check_for_errors(tcx.sess); + drop(self.coordinator_send.send(Box::new(Message::CodegenComplete))); + } + + pub fn check_for_errors(&self, sess: &Session) { + self.shared_emitter_main.check(sess, false); + } + + pub fn wait_for_signal_to_codegen_item(&self) { + match self.codegen_worker_receive.recv() { + Ok(Message::CodegenItem) => { + // Nothing to do + } + Ok(_) => panic!("unexpected message"), + Err(_) => { + // One of the LLVM threads must have panicked, fall through so + // error handling can be reached. + } + } + } +} + +pub(crate) fn submit_codegened_module_to_llvm(tcx: TyCtxt, + module: ModuleCodegen, + cost: u64) { + let llvm_work_item = WorkItem::Optimize(module); + drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::CodegenDone { + llvm_work_item, + cost, + }))); +} + +fn msvc_imps_needed(tcx: TyCtxt) -> bool { + tcx.sess.target.target.options.is_like_msvc && + tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) +} + +// Create a `__imp_ = &symbol` global for every public static `symbol`. +// This is required to satisfy `dllimport` references to static data in .rlibs +// when using MSVC linker. We do this only for data, as linker can fix up +// code references on its own. +// See #26591, #27438 +fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { + if !cgcx.msvc_imps_needed { + return + } + // The x86 ABI seems to require that leading underscores are added to symbol + // names, so we need an extra underscore on 32-bit. There's also a leading + // '\x01' here which disables LLVM's symbol mangling (e.g. no extra + // underscores added in front). + let prefix = if cgcx.target_pointer_width == "32" { + "\x01__imp__" + } else { + "\x01__imp_" + }; + unsafe { + let i8p_ty = Type::i8p_llcx(llcx); + let globals = base::iter_globals(llmod) + .filter(|&val| { + llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && + llvm::LLVMIsDeclaration(val) == 0 + }) + .map(move |val| { + let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); + let mut imp_name = prefix.as_bytes().to_vec(); + imp_name.extend(name.to_bytes()); + let imp_name = CString::new(imp_name).unwrap(); + (imp_name, val) + }) + .collect::>(); + for (imp_name, val) in globals { + let imp = llvm::LLVMAddGlobal(llmod, + i8p_ty.to_ref(), + imp_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); + llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); + } + } +} diff --git a/src/librustc_codegen_llvm/base.rs b/src/librustc_codegen_llvm/base.rs new file mode 100644 index 00000000000..d04cb72d52d --- /dev/null +++ b/src/librustc_codegen_llvm/base.rs @@ -0,0 +1,1411 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Codegen the completed AST to the LLVM IR. +//! +//! Some functions here, such as codegen_block and codegen_expr, return a value -- +//! the result of the codegen to LLVM -- while others, such as codegen_fn +//! and mono_item, are called only for the side effect of adding a +//! particular definition to the LLVM IR output we're producing. +//! +//! Hopefully useful general knowledge about codegen: +//! +//! * There's no way to find out the Ty type of a ValueRef. Doing so +//! would be "trying to get the eggs out of an omelette" (credit: +//! pcwalton). You can, instead, find out its TypeRef by calling val_ty, +//! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, +//! int) and rec(x=int, y=int, z=int) will have the same TypeRef. + +use super::ModuleLlvm; +use super::ModuleSource; +use super::ModuleCodegen; +use super::ModuleKind; + +use abi; +use back::link; +use back::write::{self, OngoingCodegen, create_target_machine}; +use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; +use llvm; +use metadata; +use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; +use rustc::middle::lang_items::StartFnLangItem; +use rustc::middle::weak_lang_items; +use rustc::mir::mono::{Linkage, Visibility, Stats}; +use rustc::middle::cstore::{EncodedMetadata}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::ty::maps::Providers; +use rustc::dep_graph::{DepNode, DepConstructor}; +use rustc::ty::subst::Kind; +use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; +use rustc::middle::exported_symbols; +use rustc::util::common::{time, print_time_passes_entry}; +use rustc::session::config::{self, NoDebugInfo}; +use rustc::session::Session; +use rustc_incremental; +use allocator; +use mir::place::PlaceRef; +use attributes; +use builder::{Builder, MemFlags}; +use callee; +use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; +use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; +use common::{self, C_struct_in_context, C_array, val_ty}; +use consts; +use context::{self, CodegenCx}; +use debuginfo; +use declare; +use meth; +use mir; +use monomorphize::Instance; +use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; +use rustc_codegen_utils::symbol_names_test; +use time_graph; +use mono_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames}; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; +use CrateInfo; +use rustc_data_structures::sync::Lrc; +use rustc_target::spec::TargetTriple; + +use std::any::Any; +use std::collections::BTreeMap; +use std::ffi::CString; +use std::str; +use std::sync::Arc; +use std::time::{Instant, Duration}; +use std::i32; +use std::cmp; +use std::sync::mpsc; +use syntax_pos::Span; +use syntax_pos::symbol::InternedString; +use syntax::attr; +use rustc::hir; +use syntax::ast; + +use mir::operand::OperandValue; + +pub use rustc_codegen_utils::check_for_rustc_errors_attr; + +pub struct StatRecorder<'a, 'tcx: 'a> { + cx: &'a CodegenCx<'a, 'tcx>, + name: Option, + istart: usize, +} + +impl<'a, 'tcx> StatRecorder<'a, 'tcx> { + pub fn new(cx: &'a CodegenCx<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { + let istart = cx.stats.borrow().n_llvm_insns; + StatRecorder { + cx, + name: Some(name), + istart, + } + } +} + +impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { + fn drop(&mut self) { + if self.cx.sess().codegen_stats() { + let mut stats = self.cx.stats.borrow_mut(); + let iend = stats.n_llvm_insns; + stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); + stats.n_fns += 1; + // Reset LLVM insn count to avoid compound costs. + stats.n_llvm_insns = self.istart; + } + } +} + +pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, + signed: bool) + -> llvm::IntPredicate { + match op { + hir::BiEq => llvm::IntEQ, + hir::BiNe => llvm::IntNE, + hir::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, + hir::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, + hir::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, + hir::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, + op => { + bug!("comparison_op_to_icmp_predicate: expected comparison operator, \ + found {:?}", + op) + } + } +} + +pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { + match op { + hir::BiEq => llvm::RealOEQ, + hir::BiNe => llvm::RealUNE, + hir::BiLt => llvm::RealOLT, + hir::BiLe => llvm::RealOLE, + hir::BiGt => llvm::RealOGT, + hir::BiGe => llvm::RealOGE, + op => { + bug!("comparison_op_to_fcmp_predicate: expected comparison operator, \ + found {:?}", + op); + } + } +} + +pub fn compare_simd_types<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef, + t: Ty<'tcx>, + ret_ty: Type, + op: hir::BinOp_ +) -> ValueRef { + let signed = match t.sty { + ty::TyFloat(_) => { + let cmp = bin_op_to_fcmp_predicate(op); + return bx.sext(bx.fcmp(cmp, lhs, rhs), ret_ty); + }, + ty::TyUint(_) => false, + ty::TyInt(_) => true, + _ => bug!("compare_simd_types: invalid SIMD type"), + }; + + let cmp = bin_op_to_icmp_predicate(op, signed); + // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension + // to get the correctly sized type. This will compile to a single instruction + // once the IR is converted to assembly if the SIMD instruction is supported + // by the target architecture. + bx.sext(bx.icmp(cmp, lhs, rhs), ret_ty) +} + +/// Retrieve the information we are losing (making dynamic) in an unsizing +/// adjustment. +/// +/// The `old_info` argument is a bit funny. It is intended for use +/// in an upcast, where the new vtable for an object will be derived +/// from the old one. +pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, + source: Ty<'tcx>, + target: Ty<'tcx>, + old_info: Option) + -> ValueRef { + let (source, target) = cx.tcx.struct_lockstep_tails(source, target); + match (&source.sty, &target.sty) { + (&ty::TyArray(_, len), &ty::TySlice(_)) => { + C_usize(cx, len.unwrap_usize(cx.tcx)) + } + (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { + // For now, upcasts are limited to changes in marker + // traits, and hence never actually require an actual + // change to the vtable. + old_info.expect("unsized_info: missing old info for trait upcast") + } + (_, &ty::TyDynamic(ref data, ..)) => { + let vtable_ptr = cx.layout_of(cx.tcx.mk_mut_ptr(target)) + .field(cx, abi::FAT_PTR_EXTRA); + consts::ptrcast(meth::get_vtable(cx, source, data.principal()), + vtable_ptr.llvm_type(cx)) + } + _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", + source, + target), + } +} + +/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. +pub fn unsize_thin_ptr<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + src: ValueRef, + src_ty: Ty<'tcx>, + dst_ty: Ty<'tcx> +) -> (ValueRef, ValueRef) { + debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); + match (&src_ty.sty, &dst_ty.sty) { + (&ty::TyRef(_, a, _), + &ty::TyRef(_, b, _)) | + (&ty::TyRef(_, a, _), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | + (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), + &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { + assert!(bx.cx.type_is_sized(a)); + let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); + (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { + let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); + assert!(bx.cx.type_is_sized(a)); + let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); + (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { + assert_eq!(def_a, def_b); + + let src_layout = bx.cx.layout_of(src_ty); + let dst_layout = bx.cx.layout_of(dst_ty); + let mut result = None; + for i in 0..src_layout.fields.count() { + let src_f = src_layout.field(bx.cx, i); + assert_eq!(src_layout.fields.offset(i).bytes(), 0); + assert_eq!(dst_layout.fields.offset(i).bytes(), 0); + if src_f.is_zst() { + continue; + } + assert_eq!(src_layout.size, src_f.size); + + let dst_f = dst_layout.field(bx.cx, i); + assert_ne!(src_f.ty, dst_f.ty); + assert_eq!(result, None); + result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); + } + let (lldata, llextra) = result.unwrap(); + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + (bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)), + bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1))) + } + _ => bug!("unsize_thin_ptr: called on bad types"), + } +} + +/// Coerce `src`, which is a reference to a value of type `src_ty`, +/// to a value of type `dst_ty` and store the result in `dst` +pub fn coerce_unsized_into<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + src: PlaceRef<'tcx>, + dst: PlaceRef<'tcx>) { + let src_ty = src.layout.ty; + let dst_ty = dst.layout.ty; + let coerce_ptr = || { + let (base, info) = match src.load(bx).val { + OperandValue::Pair(base, info) => { + // fat-ptr to fat-ptr unsize preserves the vtable + // i.e. &'a fmt::Debug+Send => &'a fmt::Debug + // So we need to pointercast the base to ensure + // the types match up. + let thin_ptr = dst.layout.field(bx.cx, abi::FAT_PTR_ADDR); + (bx.pointercast(base, thin_ptr.llvm_type(bx.cx)), info) + } + OperandValue::Immediate(base) => { + unsize_thin_ptr(bx, base, src_ty, dst_ty) + } + OperandValue::Ref(..) => bug!() + }; + OperandValue::Pair(base, info).store(bx, dst); + }; + match (&src_ty.sty, &dst_ty.sty) { + (&ty::TyRef(..), &ty::TyRef(..)) | + (&ty::TyRef(..), &ty::TyRawPtr(..)) | + (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { + coerce_ptr() + } + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { + coerce_ptr() + } + + (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { + assert_eq!(def_a, def_b); + + for i in 0..def_a.variants[0].fields.len() { + let src_f = src.project_field(bx, i); + let dst_f = dst.project_field(bx, i); + + if dst_f.layout.is_zst() { + continue; + } + + if src_f.layout.ty == dst_f.layout.ty { + memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, + src_f.align.min(dst_f.align), MemFlags::empty()); + } else { + coerce_unsized_into(bx, src_f, dst_f); + } + } + } + _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", + src_ty, + dst_ty), + } +} + +pub fn cast_shift_expr_rhs( + cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { + cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) +} + +fn cast_shift_rhs(op: hir::BinOp_, + lhs: ValueRef, + rhs: ValueRef, + trunc: F, + zext: G) + -> ValueRef + where F: FnOnce(ValueRef, Type) -> ValueRef, + G: FnOnce(ValueRef, Type) -> ValueRef +{ + // Shifts may have any size int on the rhs + if op.is_shift() { + let mut rhs_llty = val_ty(rhs); + let mut lhs_llty = val_ty(lhs); + if rhs_llty.kind() == Vector { + rhs_llty = rhs_llty.element_type() + } + if lhs_llty.kind() == Vector { + lhs_llty = lhs_llty.element_type() + } + let rhs_sz = rhs_llty.int_width(); + let lhs_sz = lhs_llty.int_width(); + if lhs_sz < rhs_sz { + trunc(rhs, lhs_llty) + } else if lhs_sz > rhs_sz { + // FIXME (#1877: If shifting by negative + // values becomes not undefined then this is wrong. + zext(rhs, lhs_llty) + } else { + rhs + } + } else { + rhs + } +} + +/// Returns whether this session's target will use SEH-based unwinding. +/// +/// This is only true for MSVC targets, and even then the 64-bit MSVC target +/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as +/// 64-bit MinGW) instead of "full SEH". +pub fn wants_msvc_seh(sess: &Session) -> bool { + sess.target.target.options.is_like_msvc +} + +pub fn call_assume<'a, 'tcx>(bx: &Builder<'a, 'tcx>, val: ValueRef) { + let assume_intrinsic = bx.cx.get_intrinsic("llvm.assume"); + bx.call(assume_intrinsic, &[val], None); +} + +pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef { + if val_ty(val) == Type::i1(bx.cx) { + bx.zext(val, Type::i8(bx.cx)) + } else { + val + } +} + +pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { + if let layout::Abi::Scalar(ref scalar) = layout.abi { + if scalar.is_bool() { + return bx.trunc(val, Type::i1(bx.cx)); + } + } + val +} + +pub fn call_memcpy(bx: &Builder, + dst: ValueRef, + src: ValueRef, + n_bytes: ValueRef, + align: Align, + flags: MemFlags) { + if flags.contains(MemFlags::NONTEMPORAL) { + // HACK(nox): This is inefficient but there is no nontemporal memcpy. + let val = bx.load(src, align); + let ptr = bx.pointercast(dst, val_ty(val).ptr_to()); + bx.store_with_flags(val, ptr, align, flags); + return; + } + let cx = bx.cx; + let ptr_width = &cx.sess().target.target.target_pointer_width; + let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); + let memcpy = cx.get_intrinsic(&key); + let src_ptr = bx.pointercast(src, Type::i8p(cx)); + let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); + let size = bx.intcast(n_bytes, cx.isize_ty, false); + let align = C_i32(cx, align.abi() as i32); + let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE)); + bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); +} + +pub fn memcpy_ty<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + dst: ValueRef, + src: ValueRef, + layout: TyLayout<'tcx>, + align: Align, + flags: MemFlags, +) { + let size = layout.size.bytes(); + if size == 0 { + return; + } + + call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags); +} + +pub fn call_memset<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + ptr: ValueRef, + fill_byte: ValueRef, + size: ValueRef, + align: ValueRef, + volatile: bool) -> ValueRef { + let ptr_width = &bx.cx.sess().target.target.target_pointer_width; + let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); + let llintrinsicfn = bx.cx.get_intrinsic(&intrinsic_key); + let volatile = C_bool(bx.cx, volatile); + bx.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) +} + +pub fn codegen_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>) { + let _s = if cx.sess().codegen_stats() { + let mut instance_name = String::new(); + DefPathBasedNames::new(cx.tcx, true, true) + .push_def_path(instance.def_id(), &mut instance_name); + Some(StatRecorder::new(cx, instance_name)) + } else { + None + }; + + // this is an info! to allow collecting monomorphization statistics + // and to allow finding the last function before LLVM aborts from + // release builds. + info!("codegen_instance({})", instance); + + let fn_ty = instance.ty(cx.tcx); + let sig = common::ty_fn_sig(cx, fn_ty); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + + let lldecl = match cx.instances.borrow().get(&instance) { + Some(&val) => val, + None => bug!("Instance `{:?}` not already declared", instance) + }; + + cx.stats.borrow_mut().n_closures += 1; + + // The `uwtable` attribute according to LLVM is: + // + // This attribute indicates that the ABI being targeted requires that an + // unwind table entry be produced for this function even if we can show + // that no exceptions passes by it. This is normally the case for the + // ELF x86-64 abi, but it can be disabled for some compilation units. + // + // Typically when we're compiling with `-C panic=abort` (which implies this + // `no_landing_pads` check) we don't need `uwtable` because we can't + // generate any exceptions! On Windows, however, exceptions include other + // events such as illegal instructions, segfaults, etc. This means that on + // Windows we end up still needing the `uwtable` attribute even if the `-C + // panic=abort` flag is passed. + // + // You can also find more info on why Windows is whitelisted here in: + // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 + if !cx.sess().no_landing_pads() || + cx.sess().target.target.options.requires_uwtable { + attributes::emit_uwtable(lldecl, true); + } + + let mir = cx.tcx.instance_mir(instance.def); + mir::codegen_mir(cx, lldecl, &mir, instance, sig); +} + +pub fn set_link_section(cx: &CodegenCx, + llval: ValueRef, + attrs: &[ast::Attribute]) { + if let Some(sect) = attr::first_attr_value_str_by_name(attrs, "link_section") { + if contains_null(§.as_str()) { + cx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", §)); + } + unsafe { + let buf = CString::new(sect.as_str().as_bytes()).unwrap(); + llvm::LLVMSetSection(llval, buf.as_ptr()); + } + } +} + +/// Create the `main` function which will initialize the rust runtime and call +/// users main function. +fn maybe_create_entry_wrapper(cx: &CodegenCx) { + let (main_def_id, span) = match *cx.sess().entry_fn.borrow() { + Some((id, span, _)) => { + (cx.tcx.hir.local_def_id(id), span) + } + None => return, + }; + + let instance = Instance::mono(cx.tcx, main_def_id); + + if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) { + // We want to create the wrapper in the same codegen unit as Rust's main + // function. + return; + } + + let main_llfn = callee::get_fn(cx, instance); + + let et = cx.sess().entry_fn.get().map(|e| e.2); + match et { + Some(config::EntryMain) => create_entry_fn(cx, span, main_llfn, main_def_id, true), + Some(config::EntryStart) => create_entry_fn(cx, span, main_llfn, main_def_id, false), + None => {} // Do nothing. + } + + fn create_entry_fn<'cx>(cx: &'cx CodegenCx, + sp: Span, + rust_main: ValueRef, + rust_main_def_id: DefId, + use_start_lang_item: bool) { + let llfty = Type::func(&[Type::c_int(cx), Type::i8p(cx).ptr_to()], &Type::c_int(cx)); + + let main_ret_ty = cx.tcx.fn_sig(rust_main_def_id).output(); + // Given that `main()` has no arguments, + // then its return type cannot have + // late-bound regions, since late-bound + // regions must appear in the argument + // listing. + let main_ret_ty = cx.tcx.erase_regions( + &main_ret_ty.no_late_bound_regions().unwrap(), + ); + + if declare::get_defined_value(cx, "main").is_some() { + // FIXME: We should be smart and show a better diagnostic here. + cx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") + .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") + .emit(); + cx.sess().abort_if_errors(); + bug!(); + } + let llfn = declare::declare_cfn(cx, "main", llfty); + + // `main` should respect same config for frame pointer elimination as rest of code + attributes::set_frame_pointer_elimination(cx, llfn); + + let bx = Builder::new_block(cx, llfn, "top"); + + debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(&bx); + + // Params from native main() used as args for rust start function + let param_argc = get_param(llfn, 0); + let param_argv = get_param(llfn, 1); + let arg_argc = bx.intcast(param_argc, cx.isize_ty, true); + let arg_argv = param_argv; + + let (start_fn, args) = if use_start_lang_item { + let start_def_id = cx.tcx.require_lang_item(StartFnLangItem); + let start_fn = callee::resolve_and_get_fn( + cx, + start_def_id, + cx.tcx.intern_substs(&[Kind::from(main_ret_ty)]), + ); + (start_fn, vec![bx.pointercast(rust_main, Type::i8p(cx).ptr_to()), + arg_argc, arg_argv]) + } else { + debug!("using user-defined start fn"); + (rust_main, vec![arg_argc, arg_argv]) + }; + + let result = bx.call(start_fn, &args, None); + bx.ret(bx.intcast(result, Type::c_int(cx), true)); + } +} + +fn contains_null(s: &str) -> bool { + s.bytes().any(|b| b == 0) +} + +fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, + llmod_id: &str, + link_meta: &LinkMeta) + -> (ContextRef, ModuleRef, EncodedMetadata) { + use std::io::Write; + use flate2::Compression; + use flate2::write::DeflateEncoder; + + let (metadata_llcx, metadata_llmod) = unsafe { + context::create_context_and_module(tcx.sess, llmod_id) + }; + + #[derive(PartialEq, Eq, PartialOrd, Ord)] + enum MetadataKind { + None, + Uncompressed, + Compressed + } + + let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { + match *ty { + config::CrateTypeExecutable | + config::CrateTypeStaticlib | + config::CrateTypeCdylib => MetadataKind::None, + + config::CrateTypeRlib => MetadataKind::Uncompressed, + + config::CrateTypeDylib | + config::CrateTypeProcMacro => MetadataKind::Compressed, + } + }).max().unwrap(); + + if kind == MetadataKind::None { + return (metadata_llcx, + metadata_llmod, + EncodedMetadata::new()); + } + + let metadata = tcx.encode_metadata(link_meta); + if kind == MetadataKind::Uncompressed { + return (metadata_llcx, metadata_llmod, metadata); + } + + assert!(kind == MetadataKind::Compressed); + let mut compressed = tcx.metadata_encoding_version(); + DeflateEncoder::new(&mut compressed, Compression::fast()) + .write_all(&metadata.raw_data).unwrap(); + + let llmeta = C_bytes_in_context(metadata_llcx, &compressed); + let llconst = C_struct_in_context(metadata_llcx, &[llmeta], false); + let name = exported_symbols::metadata_symbol_name(tcx); + let buf = CString::new(name).unwrap(); + let llglobal = unsafe { + llvm::LLVMAddGlobal(metadata_llmod, val_ty(llconst).to_ref(), buf.as_ptr()) + }; + unsafe { + llvm::LLVMSetInitializer(llglobal, llconst); + let section_name = metadata::metadata_section_name(&tcx.sess.target.target); + let name = CString::new(section_name).unwrap(); + llvm::LLVMSetSection(llglobal, name.as_ptr()); + + // Also generate a .section directive to force no + // flags, at least for ELF outputs, so that the + // metadata doesn't get loaded into memory. + let directive = format!(".section {}", section_name); + let directive = CString::new(directive).unwrap(); + llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) + } + return (metadata_llcx, metadata_llmod, metadata); +} + +pub struct ValueIter { + cur: ValueRef, + step: unsafe extern "C" fn(ValueRef) -> ValueRef, +} + +impl Iterator for ValueIter { + type Item = ValueRef; + + fn next(&mut self) -> Option { + let old = self.cur; + if !old.is_null() { + self.cur = unsafe { (self.step)(old) }; + Some(old) + } else { + None + } + } +} + +pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { + unsafe { + ValueIter { + cur: llvm::LLVMGetFirstGlobal(llmod), + step: llvm::LLVMGetNextGlobal, + } + } +} + +pub fn codegen_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver>) + -> OngoingCodegen { + + check_for_rustc_errors_attr(tcx); + + if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { + if unsafe { !llvm::LLVMRustThinLTOAvailable() } { + tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); + } + } + + if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() || + !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) && + unsafe { !llvm::LLVMRustPGOAvailable() } + { + tcx.sess.fatal("this compiler's LLVM does not support PGO"); + } + + let crate_hash = tcx.crate_hash(LOCAL_CRATE); + let link_meta = link::build_link_meta(crate_hash); + + // Codegen the metadata. + let llmod_id = "metadata"; + let (metadata_llcx, metadata_llmod, metadata) = + time(tcx.sess, "write metadata", || { + write_metadata(tcx, llmod_id, &link_meta) + }); + + let metadata_module = ModuleCodegen { + name: link::METADATA_MODULE_NAME.to_string(), + llmod_id: llmod_id.to_string(), + source: ModuleSource::Codegened(ModuleLlvm { + llcx: metadata_llcx, + llmod: metadata_llmod, + tm: create_target_machine(tcx.sess, false), + }), + kind: ModuleKind::Metadata, + }; + + let time_graph = if tcx.sess.opts.debugging_opts.codegen_time_graph { + Some(time_graph::TimeGraph::new()) + } else { + None + }; + + // Skip crate items and just output metadata in -Z no-codegen mode. + if tcx.sess.opts.debugging_opts.no_codegen || + !tcx.sess.opts.output_types.should_codegen() { + let ongoing_codegen = write::start_async_codegen( + tcx, + time_graph.clone(), + link_meta, + metadata, + rx, + 1); + + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); + ongoing_codegen.codegen_finished(tcx); + + assert_and_save_dep_graph(tcx); + + ongoing_codegen.check_for_errors(tcx.sess); + + return ongoing_codegen; + } + + // Run the monomorphization collector and partition the collected items into + // codegen units. + let codegen_units = + tcx.collect_and_partition_mono_items(LOCAL_CRATE).1; + let codegen_units = (*codegen_units).clone(); + + // Force all codegen_unit queries so they are already either red or green + // when compile_codegen_unit accesses them. We are not able to re-execute + // the codegen_unit query from just the DepNode, so an unknown color would + // lead to having to re-execute compile_codegen_unit, possibly + // unnecessarily. + if tcx.dep_graph.is_fully_enabled() { + for cgu in &codegen_units { + tcx.codegen_unit(cgu.name().clone()); + } + } + + let ongoing_codegen = write::start_async_codegen( + tcx, + time_graph.clone(), + link_meta, + metadata, + rx, + codegen_units.len()); + + // Codegen an allocator shim, if any + let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() { + unsafe { + let llmod_id = "allocator"; + let (llcx, llmod) = + context::create_context_and_module(tcx.sess, llmod_id); + let modules = ModuleLlvm { + llmod, + llcx, + tm: create_target_machine(tcx.sess, false), + }; + time(tcx.sess, "write allocator module", || { + allocator::codegen(tcx, &modules, kind) + }); + + Some(ModuleCodegen { + name: link::ALLOCATOR_MODULE_NAME.to_string(), + llmod_id: llmod_id.to_string(), + source: ModuleSource::Codegened(modules), + kind: ModuleKind::Allocator, + }) + } + } else { + None + }; + + if let Some(allocator_module) = allocator_module { + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, allocator_module); + } + + ongoing_codegen.submit_pre_codegened_module_to_llvm(tcx, metadata_module); + + // We sort the codegen units by size. This way we can schedule work for LLVM + // a bit more efficiently. + let codegen_units = { + let mut codegen_units = codegen_units; + codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); + codegen_units + }; + + let mut total_codegen_time = Duration::new(0, 0); + let mut all_stats = Stats::default(); + + for cgu in codegen_units.into_iter() { + ongoing_codegen.wait_for_signal_to_codegen_item(); + ongoing_codegen.check_for_errors(tcx.sess); + + // First, if incremental compilation is enabled, we try to re-use the + // codegen unit from the cache. + if tcx.dep_graph.is_fully_enabled() { + let cgu_id = cgu.work_product_id(); + + // Check whether there is a previous work-product we can + // re-use. Not only must the file exist, and the inputs not + // be dirty, but the hash of the symbols we will generate must + // be the same. + if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { + let dep_node = &DepNode::new(tcx, + DepConstructor::CompileCodegenUnit(cgu.name().clone())); + + // We try to mark the DepNode::CompileCodegenUnit green. If we + // succeed it means that none of the dependencies has changed + // and we can safely re-use. + if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}.rs", cgu.name()); + + let module = ModuleCodegen { + name: cgu.name().to_string(), + source: ModuleSource::Preexisting(buf), + kind: ModuleKind::Regular, + llmod_id, + }; + tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); + write::submit_codegened_module_to_llvm(tcx, module, 0); + // Continue to next cgu, this one is done. + continue + } + } else { + // This can happen if files were deleted from the cache + // directory for some reason. We just re-compile then. + } + } + + let _timing_guard = time_graph.as_ref().map(|time_graph| { + time_graph.start(write::CODEGEN_WORKER_TIMELINE, + write::CODEGEN_WORK_PACKAGE_KIND, + &format!("codegen {}", cgu.name())) + }); + let start_time = Instant::now(); + all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); + total_codegen_time += start_time.elapsed(); + ongoing_codegen.check_for_errors(tcx.sess); + } + + ongoing_codegen.codegen_finished(tcx); + + // Since the main thread is sometimes blocked during codegen, we keep track + // -Ztime-passes output manually. + print_time_passes_entry(tcx.sess.time_passes(), + "codegen to LLVM IR", + total_codegen_time); + + if tcx.sess.opts.incremental.is_some() { + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + } + + symbol_names_test::report_symbol_names(tcx); + + if tcx.sess.codegen_stats() { + println!("--- codegen stats ---"); + println!("n_glues_created: {}", all_stats.n_glues_created); + println!("n_null_glues: {}", all_stats.n_null_glues); + println!("n_real_glues: {}", all_stats.n_real_glues); + + println!("n_fns: {}", all_stats.n_fns); + println!("n_inlines: {}", all_stats.n_inlines); + println!("n_closures: {}", all_stats.n_closures); + println!("fn stats:"); + all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); + for &(ref name, insns) in all_stats.fn_stats.iter() { + println!("{} insns, {}", insns, *name); + } + } + + if tcx.sess.count_llvm_insns() { + for (k, v) in all_stats.llvm_insns.iter() { + println!("{:7} {}", *v, *k); + } + } + + ongoing_codegen.check_for_errors(tcx.sess); + + assert_and_save_dep_graph(tcx); + ongoing_codegen +} + +fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + time(tcx.sess, + "assert dep graph", + || rustc_incremental::assert_dep_graph(tcx)); + + time(tcx.sess, + "serialize dep graph", + || rustc_incremental::save_dep_graph(tcx)); +} + +fn collect_and_partition_mono_items<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cnum: CrateNum, +) -> (Arc, Arc>>>) +{ + assert_eq!(cnum, LOCAL_CRATE); + + let collection_mode = match tcx.sess.opts.debugging_opts.print_mono_items { + Some(ref s) => { + let mode_string = s.to_lowercase(); + let mode_string = mode_string.trim(); + if mode_string == "eager" { + MonoItemCollectionMode::Eager + } else { + if mode_string != "lazy" { + let message = format!("Unknown codegen-item collection mode '{}'. \ + Falling back to 'lazy' mode.", + mode_string); + tcx.sess.warn(&message); + } + + MonoItemCollectionMode::Lazy + } + } + None => { + if tcx.sess.opts.cg.link_dead_code { + MonoItemCollectionMode::Eager + } else { + MonoItemCollectionMode::Lazy + } + } + }; + + let (items, inlining_map) = + time(tcx.sess, "monomorphization collection", || { + collector::collect_crate_mono_items(tcx, collection_mode) + }); + + tcx.sess.abort_if_errors(); + + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); + + let strategy = if tcx.sess.opts.incremental.is_some() { + PartitioningStrategy::PerModule + } else { + PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) + }; + + let codegen_units = time(tcx.sess, "codegen unit partitioning", || { + partitioning::partition(tcx, + items.iter().cloned(), + strategy, + &inlining_map) + .into_iter() + .map(Arc::new) + .collect::>() + }); + + let mono_items: DefIdSet = items.iter().filter_map(|mono_item| { + match *mono_item { + MonoItem::Fn(ref instance) => Some(instance.def_id()), + MonoItem::Static(def_id) => Some(def_id), + _ => None, + } + }).collect(); + + if tcx.sess.opts.debugging_opts.print_mono_items.is_some() { + let mut item_to_cgus = FxHashMap(); + + for cgu in &codegen_units { + for (&mono_item, &linkage) in cgu.items() { + item_to_cgus.entry(mono_item) + .or_insert(Vec::new()) + .push((cgu.name().clone(), linkage)); + } + } + + let mut item_keys: Vec<_> = items + .iter() + .map(|i| { + let mut output = i.to_string(tcx); + output.push_str(" @@"); + let mut empty = Vec::new(); + let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); + cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); + cgus.dedup(); + for &(ref cgu_name, (linkage, _)) in cgus.iter() { + output.push_str(" "); + output.push_str(&cgu_name.as_str()); + + let linkage_abbrev = match linkage { + Linkage::External => "External", + Linkage::AvailableExternally => "Available", + Linkage::LinkOnceAny => "OnceAny", + Linkage::LinkOnceODR => "OnceODR", + Linkage::WeakAny => "WeakAny", + Linkage::WeakODR => "WeakODR", + Linkage::Appending => "Appending", + Linkage::Internal => "Internal", + Linkage::Private => "Private", + Linkage::ExternalWeak => "ExternalWeak", + Linkage::Common => "Common", + }; + + output.push_str("["); + output.push_str(linkage_abbrev); + output.push_str("]"); + } + output + }) + .collect(); + + item_keys.sort(); + + for item in item_keys { + println!("MONO_ITEM {}", item); + } + } + + (Arc::new(mono_items), Arc::new(codegen_units)) +} + +impl CrateInfo { + pub fn new(tcx: TyCtxt) -> CrateInfo { + let mut info = CrateInfo { + panic_runtime: None, + compiler_builtins: None, + profiler_runtime: None, + sanitizer_runtime: None, + is_no_builtins: FxHashSet(), + native_libraries: FxHashMap(), + used_libraries: tcx.native_libraries(LOCAL_CRATE), + link_args: tcx.link_args(LOCAL_CRATE), + crate_name: FxHashMap(), + used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), + used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), + used_crate_source: FxHashMap(), + wasm_custom_sections: BTreeMap::new(), + wasm_imports: FxHashMap(), + lang_item_to_crate: FxHashMap(), + missing_lang_items: FxHashMap(), + }; + let lang_items = tcx.lang_items(); + + let load_wasm_items = tcx.sess.crate_types.borrow() + .iter() + .any(|c| *c != config::CrateTypeRlib) && + tcx.sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown"); + + if load_wasm_items { + info!("attempting to load all wasm sections"); + for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() { + let (name, contents) = fetch_wasm_section(tcx, id); + info.wasm_custom_sections.entry(name) + .or_insert(Vec::new()) + .extend(contents); + } + info.load_wasm_imports(tcx, LOCAL_CRATE); + } + + for &cnum in tcx.crates().iter() { + info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); + info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); + info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); + if tcx.is_panic_runtime(cnum) { + info.panic_runtime = Some(cnum); + } + if tcx.is_compiler_builtins(cnum) { + info.compiler_builtins = Some(cnum); + } + if tcx.is_profiler_runtime(cnum) { + info.profiler_runtime = Some(cnum); + } + if tcx.is_sanitizer_runtime(cnum) { + info.sanitizer_runtime = Some(cnum); + } + if tcx.is_no_builtins(cnum) { + info.is_no_builtins.insert(cnum); + } + if load_wasm_items { + for &id in tcx.wasm_custom_sections(cnum).iter() { + let (name, contents) = fetch_wasm_section(tcx, id); + info.wasm_custom_sections.entry(name) + .or_insert(Vec::new()) + .extend(contents); + } + info.load_wasm_imports(tcx, cnum); + } + let missing = tcx.missing_lang_items(cnum); + for &item in missing.iter() { + if let Ok(id) = lang_items.require(item) { + info.lang_item_to_crate.insert(item, id.krate); + } + } + + // No need to look for lang items that are whitelisted and don't + // actually need to exist. + let missing = missing.iter() + .cloned() + .filter(|&l| !weak_lang_items::whitelisted(tcx, l)) + .collect(); + info.missing_lang_items.insert(cnum, missing); + } + + return info + } + + fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) { + for (&id, module) in tcx.wasm_import_module_map(cnum).iter() { + let instance = Instance::mono(tcx, id); + let import_name = tcx.symbol_name(instance); + self.wasm_imports.insert(import_name.to_string(), module.clone()); + } + } +} + +fn is_codegened_item(tcx: TyCtxt, id: DefId) -> bool { + let (all_mono_items, _) = + tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all_mono_items.contains(&id) +} + +fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + cgu: InternedString) -> Stats { + let cgu = tcx.codegen_unit(cgu); + + let start_time = Instant::now(); + let (stats, module) = module_codegen(tcx, cgu); + let time_to_codegen = start_time.elapsed(); + + // We assume that the cost to run LLVM on a CGU is proportional to + // the time we needed for codegenning it. + let cost = time_to_codegen.as_secs() * 1_000_000_000 + + time_to_codegen.subsec_nanos() as u64; + + write::submit_codegened_module_to_llvm(tcx, + module, + cost); + return stats; + + fn module_codegen<'a, 'tcx>( + tcx: TyCtxt<'a, 'tcx, 'tcx>, + cgu: Arc>) + -> (Stats, ModuleCodegen) + { + let cgu_name = cgu.name().to_string(); + + // Append ".rs" to LLVM module identifier. + // + // LLVM code generator emits a ".file filename" directive + // for ELF backends. Value of the "filename" is set as the + // LLVM module identifier. Due to a LLVM MC bug[1], LLVM + // crashes if the module identifier is same as other symbols + // such as a function name in the module. + // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 + let llmod_id = format!("{}-{}.rs", + cgu.name(), + tcx.crate_disambiguator(LOCAL_CRATE) + .to_fingerprint().to_hex()); + + // Instantiate monomorphizations without filling out definitions yet... + let cx = CodegenCx::new(tcx, cgu, &llmod_id); + let module = { + let mono_items = cx.codegen_unit + .items_in_deterministic_order(cx.tcx); + for &(mono_item, (linkage, visibility)) in &mono_items { + mono_item.predefine(&cx, linkage, visibility); + } + + // ... and now that we have everything pre-defined, fill out those definitions. + for &(mono_item, _) in &mono_items { + mono_item.define(&cx); + } + + // If this codegen unit contains the main function, also create the + // wrapper here + maybe_create_entry_wrapper(&cx); + + // Run replace-all-uses-with for statics that need it + for &(old_g, new_g) in cx.statics_to_rauw.borrow().iter() { + unsafe { + let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); + llvm::LLVMReplaceAllUsesWith(old_g, bitcast); + llvm::LLVMDeleteGlobal(old_g); + } + } + + // Create the llvm.used variable + // This variable has type [N x i8*] and is stored in the llvm.metadata section + if !cx.used_statics.borrow().is_empty() { + let name = CString::new("llvm.used").unwrap(); + let section = CString::new("llvm.metadata").unwrap(); + let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); + + unsafe { + let g = llvm::LLVMAddGlobal(cx.llmod, + val_ty(array).to_ref(), + name.as_ptr()); + llvm::LLVMSetInitializer(g, array); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); + llvm::LLVMSetSection(g, section.as_ptr()); + } + } + + // Finalize debuginfo + if cx.sess().opts.debuginfo != NoDebugInfo { + debuginfo::finalize(&cx); + } + + let llvm_module = ModuleLlvm { + llcx: cx.llcx, + llmod: cx.llmod, + tm: create_target_machine(cx.sess(), false), + }; + + ModuleCodegen { + name: cgu_name, + source: ModuleSource::Codegened(llvm_module), + kind: ModuleKind::Regular, + llmod_id, + } + }; + + (cx.into_stats(), module) + } +} + +pub fn provide(providers: &mut Providers) { + providers.collect_and_partition_mono_items = + collect_and_partition_mono_items; + + providers.is_codegened_item = is_codegened_item; + + providers.codegen_unit = |tcx, name| { + let (_, all) = tcx.collect_and_partition_mono_items(LOCAL_CRATE); + all.iter() + .find(|cgu| *cgu.name() == name) + .cloned() + .expect(&format!("failed to find cgu with name {:?}", name)) + }; + providers.compile_codegen_unit = compile_codegen_unit; + + provide_extern(providers); +} + +pub fn provide_extern(providers: &mut Providers) { + providers.dllimport_foreign_items = |tcx, krate| { + let module_map = tcx.foreign_modules(krate); + let module_map = module_map.iter() + .map(|lib| (lib.def_id, lib)) + .collect::>(); + + let dllimports = tcx.native_libraries(krate) + .iter() + .filter(|lib| { + if lib.kind != cstore::NativeLibraryKind::NativeUnknown { + return false + } + let cfg = match lib.cfg { + Some(ref cfg) => cfg, + None => return true, + }; + attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) + }) + .filter_map(|lib| lib.foreign_module) + .map(|id| &module_map[&id]) + .flat_map(|module| module.foreign_items.iter().cloned()) + .collect(); + Lrc::new(dllimports) + }; + + providers.is_dllimport_foreign_item = |tcx, def_id| { + tcx.dllimport_foreign_items(def_id.krate).contains(&def_id) + }; +} + +pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { + match linkage { + Linkage::External => llvm::Linkage::ExternalLinkage, + Linkage::AvailableExternally => llvm::Linkage::AvailableExternallyLinkage, + Linkage::LinkOnceAny => llvm::Linkage::LinkOnceAnyLinkage, + Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, + Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, + Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, + Linkage::Appending => llvm::Linkage::AppendingLinkage, + Linkage::Internal => llvm::Linkage::InternalLinkage, + Linkage::Private => llvm::Linkage::PrivateLinkage, + Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, + Linkage::Common => llvm::Linkage::CommonLinkage, + } +} + +pub fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { + match linkage { + Visibility::Default => llvm::Visibility::Default, + Visibility::Hidden => llvm::Visibility::Hidden, + Visibility::Protected => llvm::Visibility::Protected, + } +} + +// FIXME(mw): Anything that is produced via DepGraph::with_task() must implement +// the HashStable trait. Normally DepGraph::with_task() calls are +// hidden behind queries, but CGU creation is a special case in two +// ways: (1) it's not a query and (2) CGU are output nodes, so their +// Fingerprints are not actually needed. It remains to be clarified +// how exactly this case will be handled in the red/green system but +// for now we content ourselves with providing a no-op HashStable +// implementation for CGUs. +mod temp_stable_hash_impls { + use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, + HashStable}; + use ModuleCodegen; + + impl HashStable for ModuleCodegen { + fn hash_stable(&self, + _: &mut HCX, + _: &mut StableHasher) { + // do nothing + } + } +} + +fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { + use rustc::mir::interpret::GlobalId; + use rustc::middle::const_val::ConstVal; + + info!("loading wasm section {:?}", id); + + let section = tcx.get_attrs(id) + .iter() + .find(|a| a.check_name("wasm_custom_section")) + .expect("missing #[wasm_custom_section] attribute") + .value_str() + .expect("malformed #[wasm_custom_section] attribute"); + + let instance = ty::Instance::mono(tcx, id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::reveal_all(); + let val = tcx.const_eval(param_env.and(cid)).unwrap(); + + let const_val = match val.val { + ConstVal::Value(val) => val, + ConstVal::Unevaluated(..) => bug!("should be evaluated"), + }; + + let alloc = tcx.const_value_to_allocation((const_val, val.ty)); + (section.to_string(), alloc.bytes.clone()) +} diff --git a/src/librustc_codegen_llvm/build.rs b/src/librustc_codegen_llvm/build.rs new file mode 100644 index 00000000000..97accbb4b8f --- /dev/null +++ b/src/librustc_codegen_llvm/build.rs @@ -0,0 +1,16 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + println!("cargo:rerun-if-changed=build.rs"); + println!("cargo:rerun-if-env-changed=CFG_VERSION"); + println!("cargo:rerun-if-env-changed=CFG_PREFIX"); + println!("cargo:rerun-if-env-changed=CFG_LLVM_ROOT"); +} diff --git a/src/librustc_codegen_llvm/builder.rs b/src/librustc_codegen_llvm/builder.rs new file mode 100644 index 00000000000..7b4998e8588 --- /dev/null +++ b/src/librustc_codegen_llvm/builder.rs @@ -0,0 +1,1425 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(dead_code)] // FFI wrappers + +use llvm; +use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; +use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; +use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; +use common::*; +use type_::Type; +use value::Value; +use libc::{c_uint, c_char}; +use rustc::ty::TyCtxt; +use rustc::ty::layout::{Align, Size}; +use rustc::session::{config, Session}; + +use std::borrow::Cow; +use std::ffi::CString; +use std::ops::Range; +use std::ptr; +use syntax_pos::Span; + +// All Builders must have an llfn associated with them +#[must_use] +pub struct Builder<'a, 'tcx: 'a> { + pub llbuilder: BuilderRef, + pub cx: &'a CodegenCx<'a, 'tcx>, +} + +impl<'a, 'tcx> Drop for Builder<'a, 'tcx> { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeBuilder(self.llbuilder); + } + } +} + +// This is a really awful way to get a zero-length c-string, but better (and a +// lot more efficient) than doing str::as_c_str("", ...) every time. +fn noname() -> *const c_char { + static CNULL: c_char = 0; + &CNULL +} + +bitflags! { + pub struct MemFlags: u8 { + const VOLATILE = 1 << 0; + const NONTEMPORAL = 1 << 1; + } +} + +impl<'a, 'tcx> Builder<'a, 'tcx> { + pub fn new_block<'b>(cx: &'a CodegenCx<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self { + let bx = Builder::with_cx(cx); + let llbb = unsafe { + let name = CString::new(name).unwrap(); + llvm::LLVMAppendBasicBlockInContext( + cx.llcx, + llfn, + name.as_ptr() + ) + }; + bx.position_at_end(llbb); + bx + } + + pub fn with_cx(cx: &'a CodegenCx<'a, 'tcx>) -> Self { + // Create a fresh builder from the crate context. + let llbuilder = unsafe { + llvm::LLVMCreateBuilderInContext(cx.llcx) + }; + Builder { + llbuilder, + cx, + } + } + + pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> { + Builder::new_block(self.cx, self.llfn(), name) + } + + pub fn sess(&self) -> &Session { + self.cx.sess() + } + + pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { + self.cx.tcx + } + + pub fn llfn(&self) -> ValueRef { + unsafe { + llvm::LLVMGetBasicBlockParent(self.llbb()) + } + } + + pub fn llbb(&self) -> BasicBlockRef { + unsafe { + llvm::LLVMGetInsertBlock(self.llbuilder) + } + } + + fn count_insn(&self, category: &str) { + if self.cx.sess().codegen_stats() { + self.cx.stats.borrow_mut().n_llvm_insns += 1; + } + if self.cx.sess().count_llvm_insns() { + *self.cx.stats + .borrow_mut() + .llvm_insns + .entry(category.to_string()) + .or_insert(0) += 1; + } + } + + pub fn set_value_name(&self, value: ValueRef, name: &str) { + let cname = CString::new(name.as_bytes()).unwrap(); + unsafe { + llvm::LLVMSetValueName(value, cname.as_ptr()); + } + } + + pub fn position_before(&self, insn: ValueRef) { + unsafe { + llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); + } + } + + pub fn position_at_end(&self, llbb: BasicBlockRef) { + unsafe { + llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); + } + } + + pub fn position_at_start(&self, llbb: BasicBlockRef) { + unsafe { + llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); + } + } + + pub fn ret_void(&self) { + self.count_insn("retvoid"); + unsafe { + llvm::LLVMBuildRetVoid(self.llbuilder); + } + } + + pub fn ret(&self, v: ValueRef) { + self.count_insn("ret"); + unsafe { + llvm::LLVMBuildRet(self.llbuilder, v); + } + } + + pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { + unsafe { + llvm::LLVMBuildAggregateRet(self.llbuilder, + ret_vals.as_ptr(), + ret_vals.len() as c_uint); + } + } + + pub fn br(&self, dest: BasicBlockRef) { + self.count_insn("br"); + unsafe { + llvm::LLVMBuildBr(self.llbuilder, dest); + } + } + + pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { + self.count_insn("condbr"); + unsafe { + llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); + } + } + + pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: usize) -> ValueRef { + unsafe { + llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) + } + } + + pub fn indirect_br(&self, addr: ValueRef, num_dests: usize) { + self.count_insn("indirectbr"); + unsafe { + llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); + } + } + + pub fn invoke(&self, + llfn: ValueRef, + args: &[ValueRef], + then: BasicBlockRef, + catch: BasicBlockRef, + bundle: Option<&OperandBundleDef>) -> ValueRef { + self.count_insn("invoke"); + + debug!("Invoke {:?} with args ({})", + Value(llfn), + args.iter() + .map(|&v| format!("{:?}", Value(v))) + .collect::>() + .join(", ")); + + let args = self.check_call("invoke", llfn, args); + let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); + + unsafe { + llvm::LLVMRustBuildInvoke(self.llbuilder, + llfn, + args.as_ptr(), + args.len() as c_uint, + then, + catch, + bundle, + noname()) + } + } + + pub fn unreachable(&self) { + self.count_insn("unreachable"); + unsafe { + llvm::LLVMBuildUnreachable(self.llbuilder); + } + } + + /* Arithmetic */ + pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("add"); + unsafe { + llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswadd"); + unsafe { + llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwadd"); + unsafe { + llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fadd"); + unsafe { + llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fadd_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fadd"); + unsafe { + let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nwsub"); + unsafe { + llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwsub"); + unsafe { + llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fsub_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sub"); + unsafe { + let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("mul"); + unsafe { + llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nswmul"); + unsafe { + llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("nuwmul"); + unsafe { + llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fmul"); + unsafe { + llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fmul_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fmul"); + unsafe { + let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + + pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("udiv"); + unsafe { + llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn exactudiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("exactudiv"); + unsafe { + llvm::LLVMBuildExactUDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("sdiv"); + unsafe { + llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("exactsdiv"); + unsafe { + llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fdiv"); + unsafe { + llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn fdiv_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fdiv"); + unsafe { + let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("urem"); + unsafe { + llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("srem"); + unsafe { + llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("frem"); + unsafe { + llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn frem_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("frem"); + unsafe { + let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()); + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + + pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("shl"); + unsafe { + llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("lshr"); + unsafe { + llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ashr"); + unsafe { + llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("and"); + unsafe { + llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("or"); + unsafe { + llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("xor"); + unsafe { + llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) + -> ValueRef { + self.count_insn("binop"); + unsafe { + llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) + } + } + + pub fn neg(&self, v: ValueRef) -> ValueRef { + self.count_insn("neg"); + unsafe { + llvm::LLVMBuildNeg(self.llbuilder, v, noname()) + } + } + + pub fn nswneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nswneg"); + unsafe { + llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) + } + } + + pub fn nuwneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("nuwneg"); + unsafe { + llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) + } + } + pub fn fneg(&self, v: ValueRef) -> ValueRef { + self.count_insn("fneg"); + unsafe { + llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) + } + } + + pub fn not(&self, v: ValueRef) -> ValueRef { + self.count_insn("not"); + unsafe { + llvm::LLVMBuildNot(self.llbuilder, v, noname()) + } + } + + pub fn alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { + let bx = Builder::with_cx(self.cx); + bx.position_at_start(unsafe { + llvm::LLVMGetFirstBasicBlock(self.llfn()) + }); + bx.dynamic_alloca(ty, name, align) + } + + pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { + self.count_insn("alloca"); + unsafe { + let alloca = if name.is_empty() { + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) + } else { + let name = CString::new(name).unwrap(); + llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), + name.as_ptr()) + }; + llvm::LLVMSetAlignment(alloca, align.abi() as c_uint); + alloca + } + } + + pub fn free(&self, ptr: ValueRef) { + self.count_insn("free"); + unsafe { + llvm::LLVMBuildFree(self.llbuilder, ptr); + } + } + + pub fn load(&self, ptr: ValueRef, align: Align) -> ValueRef { + self.count_insn("load"); + unsafe { + let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + llvm::LLVMSetAlignment(load, align.abi() as c_uint); + load + } + } + + pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { + self.count_insn("load.volatile"); + unsafe { + let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); + llvm::LLVMSetVolatile(insn, llvm::True); + insn + } + } + + pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) -> ValueRef { + self.count_insn("load.atomic"); + unsafe { + let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // However, 64-bit atomic loads on `i686-apple-darwin` appear to + // require `___atomic_load` with ABI-alignment, so it's staying. + llvm::LLVMSetAlignment(load, align.pref() as c_uint); + load + } + } + + + pub fn range_metadata(&self, load: ValueRef, range: Range) { + unsafe { + let llty = val_ty(load); + let v = [ + C_uint_big(llty, range.start), + C_uint_big(llty, range.end) + ]; + + llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, + v.as_ptr(), + v.len() as c_uint)); + } + } + + pub fn nonnull_metadata(&self, load: ValueRef) { + unsafe { + llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); + } + } + + pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Align) -> ValueRef { + self.store_with_flags(val, ptr, align, MemFlags::empty()) + } + + pub fn store_with_flags( + &self, + val: ValueRef, + ptr: ValueRef, + align: Align, + flags: MemFlags, + ) -> ValueRef { + debug!("Store {:?} -> {:?} ({:?})", Value(val), Value(ptr), flags); + assert!(!self.llbuilder.is_null()); + self.count_insn("store"); + let ptr = self.check_store(val, ptr); + unsafe { + let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); + llvm::LLVMSetAlignment(store, align.abi() as c_uint); + if flags.contains(MemFlags::VOLATILE) { + llvm::LLVMSetVolatile(store, llvm::True); + } + if flags.contains(MemFlags::NONTEMPORAL) { + // According to LLVM [1] building a nontemporal store must + // *always* point to a metadata value of the integer 1. + // + // [1]: http://llvm.org/docs/LangRef.html#store-instruction + let one = C_i32(self.cx, 1); + let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); + llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); + } + store + } + } + + pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, + order: AtomicOrdering, align: Align) { + debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); + self.count_insn("store.atomic"); + let ptr = self.check_store(val, ptr); + unsafe { + let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); + // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? + // Also see `atomic_load` for more context. + llvm::LLVMSetAlignment(store, align.pref() as c_uint); + } + } + + pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("gep"); + unsafe { + llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), + indices.len() as c_uint, noname()) + } + } + + pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { + self.count_insn("inboundsgep"); + unsafe { + llvm::LLVMBuildInBoundsGEP( + self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) + } + } + + pub fn struct_gep(&self, ptr: ValueRef, idx: u64) -> ValueRef { + self.count_insn("structgep"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) + } + } + + pub fn global_string(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstring"); + unsafe { + llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) + } + } + + pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { + self.count_insn("globalstringptr"); + unsafe { + llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) + } + } + + /* Casts */ + pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("trunc"); + unsafe { + llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zext"); + unsafe { + llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sext"); + unsafe { + llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptoui"); + unsafe { + llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptosi"); + unsafe { + llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) + } + } + + pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("uitofp"); + unsafe { + llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sitofp"); + unsafe { + llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fptrunc"); + unsafe { + llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpext"); + unsafe { + llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("ptrtoint"); + unsafe { + llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("inttoptr"); + unsafe { + llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("bitcast"); + unsafe { + llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("zextorbitcast"); + unsafe { + llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("sextorbitcast"); + unsafe { + llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("truncorbitcast"); + unsafe { + llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("cast"); + unsafe { + llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) + } + } + + pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("pointercast"); + unsafe { + llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef { + self.count_insn("intcast"); + unsafe { + llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed) + } + } + + pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { + self.count_insn("fpcast"); + unsafe { + llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) + } + } + + + /* Comparisons */ + pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("icmp"); + unsafe { + llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("fcmp"); + unsafe { + llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) + } + } + + /* Miscellaneous instructions */ + pub fn empty_phi(&self, ty: Type) -> ValueRef { + self.count_insn("emptyphi"); + unsafe { + llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) + } + } + + pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { + assert_eq!(vals.len(), bbs.len()); + let phi = self.empty_phi(ty); + self.count_insn("addincoming"); + unsafe { + llvm::LLVMAddIncoming(phi, vals.as_ptr(), + bbs.as_ptr(), + vals.len() as c_uint); + phi + } + } + + pub fn add_span_comment(&self, sp: Span, text: &str) { + if self.cx.sess().asm_comments() { + let s = format!("{} ({})", + text, + self.cx.sess().codemap().span_to_string(sp)); + debug!("{}", s); + self.add_comment(&s); + } + } + + pub fn add_comment(&self, text: &str) { + if self.cx.sess().asm_comments() { + let sanitized = text.replace("$", ""); + let comment_text = format!("{} {}", "#", + sanitized.replace("\n", "\n\t# ")); + self.count_insn("inlineasm"); + let comment_text = CString::new(comment_text).unwrap(); + let asm = unsafe { + llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.cx)).to_ref(), + comment_text.as_ptr(), noname(), False, + False) + }; + self.call(asm, &[], None); + } + } + + pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, + inputs: &[ValueRef], output: Type, + volatile: bool, alignstack: bool, + dia: AsmDialect) -> ValueRef { + self.count_insn("inlineasm"); + + let volatile = if volatile { llvm::True } + else { llvm::False }; + let alignstack = if alignstack { llvm::True } + else { llvm::False }; + + let argtys = inputs.iter().map(|v| { + debug!("Asm Input Type: {:?}", Value(*v)); + val_ty(*v) + }).collect::>(); + + debug!("Asm Output Type: {:?}", output); + let fty = Type::func(&argtys[..], &output); + unsafe { + let v = llvm::LLVMRustInlineAsm( + fty.to_ref(), asm, cons, volatile, alignstack, dia); + self.call(v, inputs, None) + } + } + + pub fn call(&self, llfn: ValueRef, args: &[ValueRef], + bundle: Option<&OperandBundleDef>) -> ValueRef { + self.count_insn("call"); + + debug!("Call {:?} with args ({})", + Value(llfn), + args.iter() + .map(|&v| format!("{:?}", Value(v))) + .collect::>() + .join(", ")); + + let args = self.check_call("call", llfn, args); + let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); + + unsafe { + llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(), + args.len() as c_uint, bundle, noname()) + } + } + + pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("minnum"); + unsafe { + let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs); + if instr.is_null() { + bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0"); + } + instr + } + } + pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("maxnum"); + unsafe { + let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs); + if instr.is_null() { + bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0"); + } + instr + } + } + + pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { + self.count_insn("select"); + unsafe { + llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) + } + } + + pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { + self.count_insn("vaarg"); + unsafe { + llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) + } + } + + pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("extractelement"); + unsafe { + llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) + } + } + + pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { + self.count_insn("insertelement"); + unsafe { + llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) + } + } + + pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { + self.count_insn("shufflevector"); + unsafe { + llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) + } + } + + pub fn vector_splat(&self, num_elts: usize, elt: ValueRef) -> ValueRef { + unsafe { + let elt_ty = val_ty(elt); + let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); + let vec = self.insert_element(undef, elt, C_i32(self.cx, 0)); + let vec_i32_ty = Type::vector(&Type::i32(self.cx), num_elts as u64); + self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) + } + } + + pub fn vector_reduce_fadd_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fadd_fast"); + unsafe { + // FIXME: add a non-fast math version once + // https://bugs.llvm.org/show_bug.cgi?id=36732 + // is fixed. + let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFAdd is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_fmul_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmul_fast"); + unsafe { + // FIXME: add a non-fast math version once + // https://bugs.llvm.org/show_bug.cgi?id=36732 + // is fixed. + let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMul is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_add(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.add"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceAdd is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_mul(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.mul"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMul is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_and(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.and"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceAnd is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_or(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.or"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceOr is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_xor(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.xor"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceXor is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmin(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmin"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmax(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmax"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_fmin_fast(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmin_fast"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_fmax_fast(&self, src: ValueRef) -> ValueRef { + self.count_insn("vector.reduce.fmax_fast"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); + } + llvm::LLVMRustSetHasUnsafeAlgebra(instr); + instr + } + } + pub fn vector_reduce_min(&self, src: ValueRef, is_signed: bool) -> ValueRef { + self.count_insn("vector.reduce.min"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMin is not available in LLVM version < 5.0"); + } + instr + } + } + pub fn vector_reduce_max(&self, src: ValueRef, is_signed: bool) -> ValueRef { + self.count_insn("vector.reduce.max"); + unsafe { + let instr = llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed); + if instr.is_null() { + bug!("LLVMRustBuildVectorReduceMax is not available in LLVM version < 5.0"); + } + instr + } + } + + pub fn extract_value(&self, agg_val: ValueRef, idx: u64) -> ValueRef { + self.count_insn("extractvalue"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) + } + } + + pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, + idx: u64) -> ValueRef { + self.count_insn("insertvalue"); + assert_eq!(idx as c_uint as u64, idx); + unsafe { + llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, + noname()) + } + } + + pub fn is_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnull"); + unsafe { + llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) + } + } + + pub fn is_not_null(&self, val: ValueRef) -> ValueRef { + self.count_insn("isnotnull"); + unsafe { + llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) + } + } + + pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { + self.count_insn("ptrdiff"); + unsafe { + llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) + } + } + + pub fn trap(&self) { + unsafe { + let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); + let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); + let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); + let p = "llvm.trap\0".as_ptr(); + let t: ValueRef = llvm::LLVMGetNamedFunction(m, p as *const _); + assert!((t as isize != 0)); + let args: &[ValueRef] = &[]; + self.count_insn("trap"); + llvm::LLVMRustBuildCall(self.llbuilder, t, + args.as_ptr(), args.len() as c_uint, + ptr::null_mut(), + noname()); + } + } + + pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, + num_clauses: usize) -> ValueRef { + self.count_insn("landingpad"); + unsafe { + llvm::LLVMBuildLandingPad(self.llbuilder, ty.to_ref(), pers_fn, + num_clauses as c_uint, noname()) + } + } + + pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { + unsafe { + llvm::LLVMAddClause(landing_pad, clause); + } + } + + pub fn set_cleanup(&self, landing_pad: ValueRef) { + self.count_insn("setcleanup"); + unsafe { + llvm::LLVMSetCleanup(landing_pad, llvm::True); + } + } + + pub fn resume(&self, exn: ValueRef) -> ValueRef { + self.count_insn("resume"); + unsafe { + llvm::LLVMBuildResume(self.llbuilder, exn) + } + } + + pub fn cleanup_pad(&self, + parent: Option, + args: &[ValueRef]) -> ValueRef { + self.count_insn("cleanuppad"); + let parent = parent.unwrap_or(ptr::null_mut()); + let name = CString::new("cleanuppad").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCleanupPad(self.llbuilder, + parent, + args.len() as c_uint, + args.as_ptr(), + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for cleanuppad"); + return ret + } + + pub fn cleanup_ret(&self, cleanup: ValueRef, + unwind: Option) -> ValueRef { + self.count_insn("cleanupret"); + let unwind = unwind.unwrap_or(ptr::null_mut()); + let ret = unsafe { + llvm::LLVMRustBuildCleanupRet(self.llbuilder, cleanup, unwind) + }; + assert!(!ret.is_null(), "LLVM does not have support for cleanupret"); + return ret + } + + pub fn catch_pad(&self, + parent: ValueRef, + args: &[ValueRef]) -> ValueRef { + self.count_insn("catchpad"); + let name = CString::new("catchpad").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCatchPad(self.llbuilder, parent, + args.len() as c_uint, args.as_ptr(), + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchpad"); + return ret + } + + pub fn catch_ret(&self, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef { + self.count_insn("catchret"); + let ret = unsafe { + llvm::LLVMRustBuildCatchRet(self.llbuilder, pad, unwind) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchret"); + return ret + } + + pub fn catch_switch(&self, + parent: Option, + unwind: Option, + num_handlers: usize) -> ValueRef { + self.count_insn("catchswitch"); + let parent = parent.unwrap_or(ptr::null_mut()); + let unwind = unwind.unwrap_or(ptr::null_mut()); + let name = CString::new("catchswitch").unwrap(); + let ret = unsafe { + llvm::LLVMRustBuildCatchSwitch(self.llbuilder, parent, unwind, + num_handlers as c_uint, + name.as_ptr()) + }; + assert!(!ret.is_null(), "LLVM does not have support for catchswitch"); + return ret + } + + pub fn add_handler(&self, catch_switch: ValueRef, handler: BasicBlockRef) { + unsafe { + llvm::LLVMRustAddHandler(catch_switch, handler); + } + } + + pub fn set_personality_fn(&self, personality: ValueRef) { + unsafe { + llvm::LLVMSetPersonalityFn(self.llfn(), personality); + } + } + + // Atomic Operations + pub fn atomic_cmpxchg(&self, dst: ValueRef, + cmp: ValueRef, src: ValueRef, + order: AtomicOrdering, + failure_order: AtomicOrdering, + weak: llvm::Bool) -> ValueRef { + unsafe { + llvm::LLVMRustBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, + order, failure_order, weak) + } + } + pub fn atomic_rmw(&self, op: AtomicRmwBinOp, + dst: ValueRef, src: ValueRef, + order: AtomicOrdering) -> ValueRef { + unsafe { + llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) + } + } + + pub fn atomic_fence(&self, order: AtomicOrdering, scope: SynchronizationScope) { + unsafe { + llvm::LLVMRustBuildAtomicFence(self.llbuilder, order, scope); + } + } + + pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { + unsafe { + llvm::LLVMAddCase(s, on_val, dest) + } + } + + pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { + unsafe { + llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); + } + } + + pub fn set_invariant_load(&self, load: ValueRef) { + unsafe { + llvm::LLVMSetMetadata(load, llvm::MD_invariant_load as c_uint, + llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); + } + } + + /// Returns the ptr value that should be used for storing `val`. + fn check_store<'b>(&self, + val: ValueRef, + ptr: ValueRef) -> ValueRef { + let dest_ptr_ty = val_ty(ptr); + let stored_ty = val_ty(val); + let stored_ptr_ty = stored_ty.ptr_to(); + + assert_eq!(dest_ptr_ty.kind(), llvm::TypeKind::Pointer); + + if dest_ptr_ty == stored_ptr_ty { + ptr + } else { + debug!("Type mismatch in store. \ + Expected {:?}, got {:?}; inserting bitcast", + dest_ptr_ty, stored_ptr_ty); + self.bitcast(ptr, stored_ptr_ty) + } + } + + /// Returns the args that should be used for a call to `llfn`. + fn check_call<'b>(&self, + typ: &str, + llfn: ValueRef, + args: &'b [ValueRef]) -> Cow<'b, [ValueRef]> { + let mut fn_ty = val_ty(llfn); + // Strip off pointers + while fn_ty.kind() == llvm::TypeKind::Pointer { + fn_ty = fn_ty.element_type(); + } + + assert!(fn_ty.kind() == llvm::TypeKind::Function, + "builder::{} not passed a function, but {:?}", typ, fn_ty); + + let param_tys = fn_ty.func_params(); + + let all_args_match = param_tys.iter() + .zip(args.iter().map(|&v| val_ty(v))) + .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); + + if all_args_match { + return Cow::Borrowed(args); + } + + let casted_args: Vec<_> = param_tys.into_iter() + .zip(args.iter()) + .enumerate() + .map(|(i, (expected_ty, &actual_val))| { + let actual_ty = val_ty(actual_val); + if expected_ty != actual_ty { + debug!("Type mismatch in function call of {:?}. \ + Expected {:?} for param {}, got {:?}; injecting bitcast", + Value(llfn), + expected_ty, i, actual_ty); + self.bitcast(actual_val, expected_ty) + } else { + actual_val + } + }) + .collect(); + + return Cow::Owned(casted_args); + } + + pub fn lifetime_start(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); + } + + pub fn lifetime_end(&self, ptr: ValueRef, size: Size) { + self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); + } + + /// If LLVM lifetime intrinsic support is enabled (i.e. optimizations + /// on), and `ptr` is nonzero-sized, then extracts the size of `ptr` + /// and the intrinsic for `lt` and passes them to `emit`, which is in + /// charge of generating code to call the passed intrinsic on whatever + /// block of generated code is targeted for the intrinsic. + /// + /// If LLVM lifetime intrinsic support is disabled (i.e. optimizations + /// off) or `ptr` is zero-sized, then no-op (does not call `emit`). + fn call_lifetime_intrinsic(&self, intrinsic: &str, ptr: ValueRef, size: Size) { + if self.cx.sess().opts.optimize == config::OptLevel::No { + return; + } + + let size = size.bytes(); + if size == 0 { + return; + } + + let lifetime_intrinsic = self.cx.get_intrinsic(intrinsic); + + let ptr = self.pointercast(ptr, Type::i8p(self.cx)); + self.call(lifetime_intrinsic, &[C_u64(self.cx, size), ptr], None); + } +} diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs new file mode 100644 index 00000000000..a3dbc450ce7 --- /dev/null +++ b/src/librustc_codegen_llvm/callee.rs @@ -0,0 +1,232 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Handles codegen of callees as well as other call-related +//! things. Callees are a superset of normal rust values and sometimes +//! have different representations. In particular, top-level fn items +//! and methods are represented as just a fn ptr and not a full +//! closure. + +use attributes; +use common::{self, CodegenCx}; +use consts; +use declare; +use llvm::{self, ValueRef}; +use monomorphize::Instance; +use type_of::LayoutLlvmExt; + +use rustc::hir::def_id::DefId; +use rustc::ty::{self, TypeFoldable}; +use rustc::ty::layout::LayoutOf; +use rustc::ty::subst::Substs; +use rustc_target::spec::PanicStrategy; + +/// Codegens a reference to a fn/method item, monomorphizing and +/// inlining as it goes. +/// +/// # Parameters +/// +/// - `cx`: the crate context +/// - `instance`: the instance to be instantiated +pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>) + -> ValueRef +{ + let tcx = cx.tcx; + + debug!("get_fn(instance={:?})", instance); + + assert!(!instance.substs.needs_infer()); + assert!(!instance.substs.has_escaping_regions()); + assert!(!instance.substs.has_param_types()); + + let fn_ty = instance.ty(cx.tcx); + if let Some(&llfn) = cx.instances.borrow().get(&instance) { + return llfn; + } + + let sym = tcx.symbol_name(instance).as_str(); + debug!("get_fn({:?}: {:?}) => {}", instance, fn_ty, sym); + + // Create a fn pointer with the substituted signature. + let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(cx, fn_ty)); + let llptrty = cx.layout_of(fn_ptr_ty).llvm_type(cx); + + let llfn = if let Some(llfn) = declare::get_declared_value(cx, &sym) { + // This is subtle and surprising, but sometimes we have to bitcast + // the resulting fn pointer. The reason has to do with external + // functions. If you have two crates that both bind the same C + // library, they may not use precisely the same types: for + // example, they will probably each declare their own structs, + // which are distinct types from LLVM's point of view (nominal + // types). + // + // Now, if those two crates are linked into an application, and + // they contain inlined code, you can wind up with a situation + // where both of those functions wind up being loaded into this + // application simultaneously. In that case, the same function + // (from LLVM's point of view) requires two types. But of course + // LLVM won't allow one function to have two types. + // + // What we currently do, therefore, is declare the function with + // one of the two types (whichever happens to come first) and then + // bitcast as needed when the function is referenced to make sure + // it has the type we expect. + // + // This can occur on either a crate-local or crate-external + // reference. It also occurs when testing libcore and in some + // other weird situations. Annoying. + if common::val_ty(llfn) != llptrty { + debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); + consts::ptrcast(llfn, llptrty) + } else { + debug!("get_fn: not casting pointer!"); + llfn + } + } else { + let llfn = declare::declare_fn(cx, &sym, fn_ty); + assert_eq!(common::val_ty(llfn), llptrty); + debug!("get_fn: not casting pointer!"); + + if instance.def.is_inline(tcx) { + attributes::inline(llfn, attributes::InlineAttr::Hint); + } + attributes::from_fn_attrs(cx, llfn, instance.def.def_id()); + + let instance_def_id = instance.def_id(); + + // Perhaps questionable, but we assume that anything defined + // *in Rust code* may unwind. Foreign items like `extern "C" { + // fn foo(); }` are assumed not to unwind **unless** they have + // a `#[unwind]` attribute. + if tcx.sess.panic_strategy() == PanicStrategy::Unwind { + if !tcx.is_foreign_item(instance_def_id) { + attributes::unwind(llfn, true); + } + } + + // Apply an appropriate linkage/visibility value to our item that we + // just declared. + // + // This is sort of subtle. Inside our codegen unit we started off + // compilation by predefining all our own `MonoItem` instances. That + // is, everything we're codegenning ourselves is already defined. That + // means that anything we're actually codegenning in this codegen unit + // will have hit the above branch in `get_declared_value`. As a result, + // we're guaranteed here that we're declaring a symbol that won't get + // defined, or in other words we're referencing a value from another + // codegen unit or even another crate. + // + // So because this is a foreign value we blanket apply an external + // linkage directive because it's coming from a different object file. + // The visibility here is where it gets tricky. This symbol could be + // referencing some foreign crate or foreign library (an `extern` + // block) in which case we want to leave the default visibility. We may + // also, though, have multiple codegen units. It could be a + // monomorphization, in which case its expected visibility depends on + // whether we are sharing generics or not. The important thing here is + // that the visibility we apply to the declaration is the same one that + // has been applied to the definition (wherever that definition may be). + unsafe { + llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); + + let is_generic = instance.substs.types().next().is_some(); + + if is_generic { + // This is a monomorphization. Its expected visibility depends + // on whether we are in share-generics mode. + + if cx.tcx.share_generics() { + // We are in share_generics mode. + + if instance_def_id.is_local() { + // This is a definition from the current crate. If the + // definition is unreachable for downstream crates or + // the current crate does not re-export generics, the + // definition of the instance will have been declared + // as `hidden`. + if cx.tcx.is_unreachable_local_definition(instance_def_id) || + !cx.tcx.local_crate_exports_generics() { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a monomorphization of a generic function + // defined in an upstream crate. + if cx.tcx.upstream_monomorphizations_for(instance_def_id) + .map(|set| set.contains_key(instance.substs)) + .unwrap_or(false) { + // This is instantiated in another crate. It cannot + // be `hidden`. + } else { + // This is a local instantiation of an upstream definition. + // If the current crate does not re-export it + // (because it is a C library or an executable), it + // will have been declared `hidden`. + if !cx.tcx.local_crate_exports_generics() { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } + } + } else { + // When not sharing generics, all instances are in the same + // crate and have hidden visibility + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a non-generic function + if cx.tcx.is_codegened_item(instance_def_id) { + // This is a function that is instantiated in the local crate + + if instance_def_id.is_local() { + // This is function that is defined in the local crate. + // If it is not reachable, it is hidden. + if !cx.tcx.is_reachable_non_generic(instance_def_id) { + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } else { + // This is a function from an upstream crate that has + // been instantiated here. These are always hidden. + llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); + } + } + } + } + + if cx.use_dll_storage_attrs && + tcx.is_dllimport_foreign_item(instance_def_id) + { + unsafe { + llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); + } + } + + llfn + }; + + cx.instances.borrow_mut().insert(instance, llfn); + + llfn +} + +pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + substs: &'tcx Substs<'tcx>) + -> ValueRef +{ + get_fn( + cx, + ty::Instance::resolve( + cx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs + ).unwrap() + ) +} diff --git a/src/librustc_codegen_llvm/common.rs b/src/librustc_codegen_llvm/common.rs new file mode 100644 index 00000000000..10ca8e43ce6 --- /dev/null +++ b/src/librustc_codegen_llvm/common.rs @@ -0,0 +1,448 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_camel_case_types, non_snake_case)] + +//! Code that is useful in various codegen modules. + +use llvm; +use llvm::{ValueRef, ContextRef, TypeKind}; +use llvm::{True, False, Bool, OperandBundleDef}; +use rustc::hir::def_id::DefId; +use rustc::middle::lang_items::LangItem; +use abi; +use base; +use builder::Builder; +use consts; +use declare; +use type_::Type; +use type_of::LayoutLlvmExt; +use value::Value; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; +use rustc::hir; + +use libc::{c_uint, c_char}; +use std::iter; + +use rustc_target::spec::abi::Abi; +use syntax::symbol::LocalInternedString; +use syntax_pos::{Span, DUMMY_SP}; + +pub use context::CodegenCx; + +pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) +} + +pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) +} + +pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { + ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP) +} + +/* +* A note on nomenclature of linking: "extern", "foreign", and "upcall". +* +* An "extern" is an LLVM symbol we wind up emitting an undefined external +* reference to. This means "we don't have the thing in this compilation unit, +* please make sure you link it in at runtime". This could be a reference to +* C code found in a C library, or rust code found in a rust crate. +* +* Most "externs" are implicitly declared (automatically) as a result of a +* user declaring an extern _module_ dependency; this causes the rust driver +* to locate an extern crate, scan its compilation metadata, and emit extern +* declarations for any symbols used by the declaring crate. +* +* A "foreign" is an extern that references C (or other non-rust ABI) code. +* There is no metadata to scan for extern references so in these cases either +* a header-digester like bindgen, or manual function prototypes, have to +* serve as declarators. So these are usually given explicitly as prototype +* declarations, in rust code, with ABI attributes on them noting which ABI to +* link via. +* +* An "upcall" is a foreign call generated by the compiler (not corresponding +* to any user-written call in the code) into the runtime library, to perform +* some helper task such as bringing a task to life, allocating memory, etc. +* +*/ + +/// A structure representing an active landing pad for the duration of a basic +/// block. +/// +/// Each `Block` may contain an instance of this, indicating whether the block +/// is part of a landing pad or not. This is used to make decision about whether +/// to emit `invoke` instructions (e.g. in a landing pad we don't continue to +/// use `invoke`) and also about various function call metadata. +/// +/// For GNU exceptions (`landingpad` + `resume` instructions) this structure is +/// just a bunch of `None` instances (not too interesting), but for MSVC +/// exceptions (`cleanuppad` + `cleanupret` instructions) this contains data. +/// When inside of a landing pad, each function call in LLVM IR needs to be +/// annotated with which landing pad it's a part of. This is accomplished via +/// the `OperandBundleDef` value created for MSVC landing pads. +pub struct Funclet { + cleanuppad: ValueRef, + operand: OperandBundleDef, +} + +impl Funclet { + pub fn new(cleanuppad: ValueRef) -> Funclet { + Funclet { + cleanuppad, + operand: OperandBundleDef::new("funclet", &[cleanuppad]), + } + } + + pub fn cleanuppad(&self) -> ValueRef { + self.cleanuppad + } + + pub fn bundle(&self) -> &OperandBundleDef { + &self.operand + } +} + +pub fn val_ty(v: ValueRef) -> Type { + unsafe { + Type::from_ref(llvm::LLVMTypeOf(v)) + } +} + +// LLVM constant constructors. +pub fn C_null(t: Type) -> ValueRef { + unsafe { + llvm::LLVMConstNull(t.to_ref()) + } +} + +pub fn C_undef(t: Type) -> ValueRef { + unsafe { + llvm::LLVMGetUndef(t.to_ref()) + } +} + +pub fn C_int(t: Type, i: i64) -> ValueRef { + unsafe { + llvm::LLVMConstInt(t.to_ref(), i as u64, True) + } +} + +pub fn C_uint(t: Type, i: u64) -> ValueRef { + unsafe { + llvm::LLVMConstInt(t.to_ref(), i, False) + } +} + +pub fn C_uint_big(t: Type, u: u128) -> ValueRef { + unsafe { + let words = [u as u64, (u >> 64) as u64]; + llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr()) + } +} + +pub fn C_bool(cx: &CodegenCx, val: bool) -> ValueRef { + C_uint(Type::i1(cx), val as u64) +} + +pub fn C_i32(cx: &CodegenCx, i: i32) -> ValueRef { + C_int(Type::i32(cx), i as i64) +} + +pub fn C_u32(cx: &CodegenCx, i: u32) -> ValueRef { + C_uint(Type::i32(cx), i as u64) +} + +pub fn C_u64(cx: &CodegenCx, i: u64) -> ValueRef { + C_uint(Type::i64(cx), i) +} + +pub fn C_usize(cx: &CodegenCx, i: u64) -> ValueRef { + let bit_size = cx.data_layout().pointer_size.bits(); + if bit_size < 64 { + // make sure it doesn't overflow + assert!(i < (1< ValueRef { + C_uint(Type::i8(cx), i as u64) +} + + +// This is a 'c-like' raw string, which differs from +// our boxed-and-length-annotated strings. +pub fn C_cstr(cx: &CodegenCx, s: LocalInternedString, null_terminated: bool) -> ValueRef { + unsafe { + if let Some(&llval) = cx.const_cstr_cache.borrow().get(&s) { + return llval; + } + + let sc = llvm::LLVMConstStringInContext(cx.llcx, + s.as_ptr() as *const c_char, + s.len() as c_uint, + !null_terminated as Bool); + let sym = cx.generate_local_symbol_name("str"); + let g = declare::define_global(cx, &sym[..], val_ty(sc)).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", sym); + }); + llvm::LLVMSetInitializer(g, sc); + llvm::LLVMSetGlobalConstant(g, True); + llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); + + cx.const_cstr_cache.borrow_mut().insert(s, g); + g + } +} + +// NB: Do not use `do_spill_noroot` to make this into a constant string, or +// you will be kicked off fast isel. See issue #4352 for an example of this. +pub fn C_str_slice(cx: &CodegenCx, s: LocalInternedString) -> ValueRef { + let len = s.len(); + let cs = consts::ptrcast(C_cstr(cx, s, false), + cx.layout_of(cx.tcx.mk_str()).llvm_type(cx).ptr_to()); + C_fat_ptr(cx, cs, C_usize(cx, len as u64)) +} + +pub fn C_fat_ptr(cx: &CodegenCx, ptr: ValueRef, meta: ValueRef) -> ValueRef { + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + C_struct(cx, &[ptr, meta], false) +} + +pub fn C_struct(cx: &CodegenCx, elts: &[ValueRef], packed: bool) -> ValueRef { + C_struct_in_context(cx.llcx, elts, packed) +} + +pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { + unsafe { + llvm::LLVMConstStructInContext(llcx, + elts.as_ptr(), elts.len() as c_uint, + packed as Bool) + } +} + +pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); + } +} + +pub fn C_vector(elts: &[ValueRef]) -> ValueRef { + unsafe { + return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); + } +} + +pub fn C_bytes(cx: &CodegenCx, bytes: &[u8]) -> ValueRef { + C_bytes_in_context(cx.llcx, bytes) +} + +pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { + unsafe { + let ptr = bytes.as_ptr() as *const c_char; + return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); + } +} + +pub fn const_get_elt(v: ValueRef, idx: u64) -> ValueRef { + unsafe { + assert_eq!(idx as c_uint as u64, idx); + let us = &[idx as c_uint]; + let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); + + debug!("const_get_elt(v={:?}, idx={}, r={:?})", + Value(v), idx, Value(r)); + + r + } +} + +pub fn const_get_real(v: ValueRef) -> Option<(f64, bool)> { + unsafe { + if is_const_real(v) { + let mut loses_info: llvm::Bool = ::std::mem::uninitialized(); + let r = llvm::LLVMConstRealGetDouble(v, &mut loses_info as *mut llvm::Bool); + let loses_info = if loses_info == 1 { true } else { false }; + Some((r, loses_info)) + } else { + None + } + } +} + +pub fn const_to_uint(v: ValueRef) -> u64 { + unsafe { + llvm::LLVMConstIntGetZExtValue(v) + } +} + +pub fn is_const_integral(v: ValueRef) -> bool { + unsafe { + !llvm::LLVMIsAConstantInt(v).is_null() + } +} + +pub fn is_const_real(v: ValueRef) -> bool { + unsafe { + !llvm::LLVMIsAConstantFP(v).is_null() + } +} + + +#[inline] +fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { + ((hi as u128) << 64) | (lo as u128) +} + +pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { + unsafe { + if is_const_integral(v) { + let (mut lo, mut hi) = (0u64, 0u64); + let success = llvm::LLVMRustConstInt128Get(v, sign_ext, + &mut hi as *mut u64, &mut lo as *mut u64); + if success { + Some(hi_lo_to_u128(lo, hi)) + } else { + None + } + } else { + None + } + } +} + +pub fn langcall(tcx: TyCtxt, + span: Option, + msg: &str, + li: LangItem) + -> DefId { + match tcx.lang_items().require(li) { + Ok(id) => id, + Err(s) => { + let msg = format!("{} {}", msg, s); + match span { + Some(span) => tcx.sess.span_fatal(span, &msg[..]), + None => tcx.sess.fatal(&msg[..]), + } + } + } +} + +// To avoid UB from LLVM, these two functions mask RHS with an +// appropriate mask unconditionally (i.e. the fallback behavior for +// all shifts). For 32- and 64-bit types, this matches the semantics +// of Java. (See related discussion on #1877 and #10183.) + +pub fn build_unchecked_lshift<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + lhs: ValueRef, + rhs: ValueRef +) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShl, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + bx.shl(lhs, rhs) +} + +pub fn build_unchecked_rshift<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef +) -> ValueRef { + let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShr, lhs, rhs); + // #1877, #10183: Ensure that input is always valid + let rhs = shift_mask_rhs(bx, rhs); + let is_signed = lhs_t.is_signed(); + if is_signed { + bx.ashr(lhs, rhs) + } else { + bx.lshr(lhs, rhs) + } +} + +fn shift_mask_rhs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { + let rhs_llty = val_ty(rhs); + bx.and(rhs, shift_mask_val(bx, rhs_llty, rhs_llty, false)) +} + +pub fn shift_mask_val<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + llty: Type, + mask_llty: Type, + invert: bool +) -> ValueRef { + let kind = llty.kind(); + match kind { + TypeKind::Integer => { + // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. + let val = llty.int_width() - 1; + if invert { + C_int(mask_llty, !val as i64) + } else { + C_uint(mask_llty, val) + } + }, + TypeKind::Vector => { + let mask = shift_mask_val(bx, llty.element_type(), mask_llty.element_type(), invert); + bx.vector_splat(mask_llty.vector_length(), mask) + }, + _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), + } +} + +pub fn ty_fn_sig<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: Ty<'tcx>) + -> ty::PolyFnSig<'tcx> +{ + match ty.sty { + ty::TyFnDef(..) | + // Shims currently have type TyFnPtr. Not sure this should remain. + ty::TyFnPtr(_) => ty.fn_sig(cx.tcx), + ty::TyClosure(def_id, substs) => { + let tcx = cx.tcx; + let sig = substs.closure_sig(def_id, tcx); + + let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); + sig.map_bound(|sig| tcx.mk_fn_sig( + iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), + sig.output(), + sig.variadic, + sig.unsafety, + sig.abi + )) + } + ty::TyGenerator(def_id, substs, _) => { + let tcx = cx.tcx; + let sig = substs.poly_sig(def_id, cx.tcx); + + let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); + let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); + + sig.map_bound(|sig| { + let state_did = tcx.lang_items().gen_state().unwrap(); + let state_adt_ref = tcx.adt_def(state_did); + let state_substs = tcx.mk_substs([sig.yield_ty.into(), + sig.return_ty.into()].iter()); + let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); + + tcx.mk_fn_sig(iter::once(env_ty), + ret_ty, + false, + hir::Unsafety::Normal, + Abi::Rust + ) + }) + } + _ => bug!("unexpected type {:?} to ty_fn_sig", ty) + } +} diff --git a/src/librustc_codegen_llvm/consts.rs b/src/librustc_codegen_llvm/consts.rs new file mode 100644 index 00000000000..afa81465ea2 --- /dev/null +++ b/src/librustc_codegen_llvm/consts.rs @@ -0,0 +1,322 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; +use llvm::{SetUnnamedAddr}; +use llvm::{ValueRef, True}; +use rustc::hir::def_id::DefId; +use rustc::hir::map as hir_map; +use debuginfo; +use base; +use monomorphize::MonoItem; +use common::{CodegenCx, val_ty}; +use declare; +use monomorphize::Instance; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::ty; +use rustc::ty::layout::{Align, LayoutOf}; + +use rustc::hir; + +use std::ffi::{CStr, CString}; +use syntax::ast; +use syntax::attr; + +pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { + unsafe { + llvm::LLVMConstPointerCast(val, ty.to_ref()) + } +} + +pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef { + unsafe { + llvm::LLVMConstBitCast(val, ty.to_ref()) + } +} + +fn set_global_alignment(cx: &CodegenCx, + gv: ValueRef, + mut align: Align) { + // The target may require greater alignment for globals than the type does. + // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, + // which can force it to be smaller. Rust doesn't support this yet. + if let Some(min) = cx.sess().target.target.options.min_global_align { + match ty::layout::Align::from_bits(min, min) { + Ok(min) => align = align.max(min), + Err(err) => { + cx.sess().err(&format!("invalid minimum global alignment: {}", err)); + } + } + } + unsafe { + llvm::LLVMSetAlignment(gv, align.abi() as u32); + } +} + +pub fn addr_of_mut(cx: &CodegenCx, + cv: ValueRef, + align: Align, + kind: &str) + -> ValueRef { + unsafe { + let name = cx.generate_local_symbol_name(kind); + let gv = declare::define_global(cx, &name[..], val_ty(cv)).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", name); + }); + llvm::LLVMSetInitializer(gv, cv); + set_global_alignment(cx, gv, align); + llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); + SetUnnamedAddr(gv, true); + gv + } +} + +pub fn addr_of(cx: &CodegenCx, + cv: ValueRef, + align: Align, + kind: &str) + -> ValueRef { + if let Some(&gv) = cx.const_globals.borrow().get(&cv) { + unsafe { + // Upgrade the alignment in cases where the same constant is used with different + // alignment requirements + let llalign = align.abi() as u32; + if llalign > llvm::LLVMGetAlignment(gv) { + llvm::LLVMSetAlignment(gv, llalign); + } + } + return gv; + } + let gv = addr_of_mut(cx, cv, align, kind); + unsafe { + llvm::LLVMSetGlobalConstant(gv, True); + } + cx.const_globals.borrow_mut().insert(cv, gv); + gv +} + +pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { + let instance = Instance::mono(cx.tcx, def_id); + if let Some(&g) = cx.instances.borrow().get(&instance) { + return g; + } + + let defined_in_current_codegen_unit = cx.codegen_unit + .items() + .contains_key(&MonoItem::Static(def_id)); + assert!(!defined_in_current_codegen_unit, + "consts::get_static() should always hit the cache for \ + statics defined in the same CGU, but did not for `{:?}`", + def_id); + + let ty = instance.ty(cx.tcx); + let sym = cx.tcx.symbol_name(instance).as_str(); + + let g = if let Some(id) = cx.tcx.hir.as_local_node_id(def_id) { + + let llty = cx.layout_of(ty).llvm_type(cx); + let (g, attrs) = match cx.tcx.hir.get(id) { + hir_map::NodeItem(&hir::Item { + ref attrs, span, node: hir::ItemStatic(..), .. + }) => { + if declare::get_declared_value(cx, &sym[..]).is_some() { + span_bug!(span, "Conflicting symbol names for static?"); + } + + let g = declare::define_global(cx, &sym[..], llty).unwrap(); + + if !cx.tcx.is_reachable_non_generic(def_id) { + unsafe { + llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); + } + } + + (g, attrs) + } + + hir_map::NodeForeignItem(&hir::ForeignItem { + ref attrs, span, node: hir::ForeignItemStatic(..), .. + }) => { + let g = if let Some(linkage) = cx.tcx.codegen_fn_attrs(def_id).linkage { + // If this is a static with a linkage specified, then we need to handle + // it a little specially. The typesystem prevents things like &T and + // extern "C" fn() from being non-null, so we can't just declare a + // static and call it a day. Some linkages (like weak) will make it such + // that the static actually has a null value. + let llty2 = match ty.sty { + ty::TyRawPtr(ref mt) => cx.layout_of(mt.ty).llvm_type(cx), + _ => { + cx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); + } + }; + unsafe { + // Declare a symbol `foo` with the desired linkage. + let g1 = declare::declare_global(cx, &sym, llty2); + llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage)); + + // Declare an internal global `extern_with_linkage_foo` which + // is initialized with the address of `foo`. If `foo` is + // discarded during linking (for example, if `foo` has weak + // linkage and there are no definitions), then + // `extern_with_linkage_foo` will instead be initialized to + // zero. + let mut real_name = "_rust_extern_with_linkage_".to_string(); + real_name.push_str(&sym); + let g2 = declare::define_global(cx, &real_name, llty).unwrap_or_else(||{ + cx.sess().span_fatal(span, + &format!("symbol `{}` is already defined", &sym)) + }); + llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); + llvm::LLVMSetInitializer(g2, g1); + g2 + } + } else { + // Generate an external declaration. + declare::declare_global(cx, &sym, llty) + }; + + (g, attrs) + } + + item => bug!("get_static: expected static, found {:?}", item) + }; + + for attr in attrs { + if attr.check_name("thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + } + + g + } else { + // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? + // FIXME(nagisa): investigate whether it can be changed into define_global + let g = declare::declare_global(cx, &sym, cx.layout_of(ty).llvm_type(cx)); + // Thread-local statics in some other crate need to *always* be linked + // against in a thread-local fashion, so we need to be sure to apply the + // thread-local attribute locally if it was present remotely. If we + // don't do this then linker errors can be generated where the linker + // complains that one object files has a thread local version of the + // symbol and another one doesn't. + for attr in cx.tcx.get_attrs(def_id).iter() { + if attr.check_name("thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + } + if cx.use_dll_storage_attrs && !cx.tcx.is_foreign_item(def_id) { + // This item is external but not foreign, i.e. it originates from an external Rust + // crate. Since we don't know whether this crate will be linked dynamically or + // statically in the final application, we always mark such symbols as 'dllimport'. + // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs to + // make things work. + // + // However, in some scenarios we defer emission of statics to downstream + // crates, so there are cases where a static with an upstream DefId + // is actually present in the current crate. We can find out via the + // is_codegened_item query. + if !cx.tcx.is_codegened_item(def_id) { + unsafe { + llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); + } + } + } + g + }; + + if cx.use_dll_storage_attrs && cx.tcx.is_dllimport_foreign_item(def_id) { + // For foreign (native) libs we know the exact storage type to use. + unsafe { + llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); + } + } + + cx.instances.borrow_mut().insert(instance, g); + cx.statics.borrow_mut().insert(g, def_id); + g +} + +pub fn codegen_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + is_mutable: bool, + attrs: &[ast::Attribute]) { + unsafe { + let g = get_static(cx, def_id); + + let v = match ::mir::codegen_static_initializer(cx, def_id) { + Ok(v) => v, + // Error has already been reported + Err(_) => return, + }; + + // boolean SSA values are i1, but they have to be stored in i8 slots, + // otherwise some LLVM optimization passes don't work as expected + let mut val_llty = val_ty(v); + let v = if val_llty == Type::i1(cx) { + val_llty = Type::i8(cx); + llvm::LLVMConstZExt(v, val_llty.to_ref()) + } else { + v + }; + + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx); + let llty = cx.layout_of(ty).llvm_type(cx); + let g = if val_llty == llty { + g + } else { + // If we created the global with the wrong type, + // correct the type. + let empty_string = CString::new("").unwrap(); + let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); + let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); + llvm::LLVMSetValueName(g, empty_string.as_ptr()); + + let linkage = llvm::LLVMRustGetLinkage(g); + let visibility = llvm::LLVMRustGetVisibility(g); + + let new_g = llvm::LLVMRustGetOrInsertGlobal( + cx.llmod, name_string.as_ptr(), val_llty.to_ref()); + + llvm::LLVMRustSetLinkage(new_g, linkage); + llvm::LLVMRustSetVisibility(new_g, visibility); + + // To avoid breaking any invariants, we leave around the old + // global for the moment; we'll replace all references to it + // with the new global later. (See base::codegen_backend.) + cx.statics_to_rauw.borrow_mut().push((g, new_g)); + new_g + }; + set_global_alignment(cx, g, cx.align_of(ty)); + llvm::LLVMSetInitializer(g, v); + + // As an optimization, all shared statics which do not have interior + // mutability are placed into read-only memory. + if !is_mutable { + if cx.type_is_freeze(ty) { + llvm::LLVMSetGlobalConstant(g, llvm::True); + } + } + + debuginfo::create_global_var_metadata(cx, def_id, g); + + if attr::contains_name(attrs, "thread_local") { + llvm::set_thread_local_mode(g, cx.tls_model); + } + + base::set_link_section(cx, g, attrs); + + if attr::contains_name(attrs, "used") { + // This static will be stored in the llvm.used variable which is an array of i8* + let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); + cx.used_statics.borrow_mut().push(cast); + } + } +} diff --git a/src/librustc_codegen_llvm/context.rs b/src/librustc_codegen_llvm/context.rs new file mode 100644 index 00000000000..9b1de3d44ea --- /dev/null +++ b/src/librustc_codegen_llvm/context.rs @@ -0,0 +1,666 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use common; +use llvm; +use llvm::{ContextRef, ModuleRef, ValueRef}; +use rustc::dep_graph::DepGraphSafe; +use rustc::hir; +use rustc::hir::def_id::DefId; +use debuginfo; +use callee; +use base; +use declare; +use monomorphize::Instance; + +use monomorphize::partitioning::CodegenUnit; +use type_::Type; +use type_of::PointeeInfo; + +use rustc_data_structures::base_n; +use rustc::mir::mono::Stats; +use rustc::session::config::{self, NoDebugInfo}; +use rustc::session::Session; +use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; +use rustc::ty::{self, Ty, TyCtxt}; +use rustc::util::nodemap::FxHashMap; +use rustc_target::spec::{HasTargetSpec, Target}; + +use std::ffi::{CStr, CString}; +use std::cell::{Cell, RefCell}; +use std::ptr; +use std::iter; +use std::str; +use std::sync::Arc; +use syntax::symbol::LocalInternedString; +use abi::Abi; + +/// There is one `CodegenCx` per compilation unit. Each one has its own LLVM +/// `ContextRef` so that several compilation units may be optimized in parallel. +/// All other LLVM data structures in the `CodegenCx` are tied to that `ContextRef`. +pub struct CodegenCx<'a, 'tcx: 'a> { + pub tcx: TyCtxt<'a, 'tcx, 'tcx>, + pub check_overflow: bool, + pub use_dll_storage_attrs: bool, + pub tls_model: llvm::ThreadLocalMode, + + pub llmod: ModuleRef, + pub llcx: ContextRef, + pub stats: RefCell, + pub codegen_unit: Arc>, + + /// Cache instances of monomorphic and polymorphic items + pub instances: RefCell, ValueRef>>, + /// Cache generated vtables + pub vtables: RefCell, + Option>), ValueRef>>, + /// Cache of constant strings, + pub const_cstr_cache: RefCell>, + + /// Reverse-direction for const ptrs cast from globals. + /// Key is a ValueRef holding a *T, + /// Val is a ValueRef holding a *[T]. + /// + /// Needed because LLVM loses pointer->pointee association + /// when we ptrcast, and we have to ptrcast during codegen + /// of a [T] const because we form a slice, a (*T,usize) pair, not + /// a pointer to an LLVM array type. Similar for trait objects. + pub const_unsized: RefCell>, + + /// Cache of emitted const globals (value -> global) + pub const_globals: RefCell>, + + /// Mapping from static definitions to their DefId's. + pub statics: RefCell>, + + /// List of globals for static variables which need to be passed to the + /// LLVM function ReplaceAllUsesWith (RAUW) when codegen is complete. + /// (We have to make sure we don't invalidate any ValueRefs referring + /// to constants.) + pub statics_to_rauw: RefCell>, + + /// Statics that will be placed in the llvm.used variable + /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details + pub used_statics: RefCell>, + + pub lltypes: RefCell, Option), Type>>, + pub scalar_lltypes: RefCell, Type>>, + pub pointee_infos: RefCell, Size), Option>>, + pub isize_ty: Type, + + pub dbg_cx: Option>, + + eh_personality: Cell>, + eh_unwind_resume: Cell>, + pub rust_try_fn: Cell>, + + intrinsics: RefCell>, + + /// A counter that is used for generating local symbol names + local_gen_sym_counter: Cell, +} + +impl<'a, 'tcx> DepGraphSafe for CodegenCx<'a, 'tcx> { +} + +pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { + let reloc_model_arg = match sess.opts.cg.relocation_model { + Some(ref s) => &s[..], + None => &sess.target.target.options.relocation_model[..], + }; + + match ::back::write::RELOC_MODEL_ARGS.iter().find( + |&&arg| arg.0 == reloc_model_arg) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid relocation mode", + reloc_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } +} + +fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { + let tls_model_arg = match sess.opts.debugging_opts.tls_model { + Some(ref s) => &s[..], + None => &sess.target.target.options.tls_model[..], + }; + + match ::back::write::TLS_MODEL_ARGS.iter().find( + |&&arg| arg.0 == tls_model_arg) { + Some(x) => x.1, + _ => { + sess.err(&format!("{:?} is not a valid TLS model", + tls_model_arg)); + sess.abort_if_errors(); + bug!(); + } + } +} + +fn is_any_library(sess: &Session) -> bool { + sess.crate_types.borrow().iter().any(|ty| { + *ty != config::CrateTypeExecutable + }) +} + +pub fn is_pie_binary(sess: &Session) -> bool { + !is_any_library(sess) && get_reloc_model(sess) == llvm::RelocMode::PIC +} + +pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { + let llcx = llvm::LLVMRustContextCreate(sess.fewer_names()); + let mod_name = CString::new(mod_name).unwrap(); + let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); + + // Ensure the data-layout values hardcoded remain the defaults. + if sess.target.target.options.is_builtin { + let tm = ::back::write::create_target_machine(sess, false); + llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); + llvm::LLVMRustDisposeTargetMachine(tm); + + let data_layout = llvm::LLVMGetDataLayout(llmod); + let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes()) + .ok().expect("got a non-UTF8 data-layout from LLVM"); + + // Unfortunately LLVM target specs change over time, and right now we + // don't have proper support to work with any more than one + // `data_layout` than the one that is in the rust-lang/rust repo. If + // this compiler is configured against a custom LLVM, we may have a + // differing data layout, even though we should update our own to use + // that one. + // + // As an interim hack, if CFG_LLVM_ROOT is not an empty string then we + // disable this check entirely as we may be configured with something + // that has a different target layout. + // + // Unsure if this will actually cause breakage when rustc is configured + // as such. + // + // FIXME(#34960) + let cfg_llvm_root = option_env!("CFG_LLVM_ROOT").unwrap_or(""); + let custom_llvm_used = cfg_llvm_root.trim() != ""; + + if !custom_llvm_used && sess.target.target.data_layout != data_layout { + bug!("data-layout for builtin `{}` target, `{}`, \ + differs from LLVM default, `{}`", + sess.target.target.llvm_target, + sess.target.target.data_layout, + data_layout); + } + } + + let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); + llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); + + let llvm_target = sess.target.target.llvm_target.as_bytes(); + let llvm_target = CString::new(llvm_target).unwrap(); + llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); + + if is_pie_binary(sess) { + llvm::LLVMRustSetModulePIELevel(llmod); + } + + (llcx, llmod) +} + +impl<'a, 'tcx> CodegenCx<'a, 'tcx> { + pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, + codegen_unit: Arc>, + llmod_id: &str) + -> CodegenCx<'a, 'tcx> { + // An interesting part of Windows which MSVC forces our hand on (and + // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` + // attributes in LLVM IR as well as native dependencies (in C these + // correspond to `__declspec(dllimport)`). + // + // Whenever a dynamic library is built by MSVC it must have its public + // interface specified by functions tagged with `dllexport` or otherwise + // they're not available to be linked against. This poses a few problems + // for the compiler, some of which are somewhat fundamental, but we use + // the `use_dll_storage_attrs` variable below to attach the `dllexport` + // attribute to all LLVM functions that are exported e.g. they're + // already tagged with external linkage). This is suboptimal for a few + // reasons: + // + // * If an object file will never be included in a dynamic library, + // there's no need to attach the dllexport attribute. Most object + // files in Rust are not destined to become part of a dll as binaries + // are statically linked by default. + // * If the compiler is emitting both an rlib and a dylib, the same + // source object file is currently used but with MSVC this may be less + // feasible. The compiler may be able to get around this, but it may + // involve some invasive changes to deal with this. + // + // The flipside of this situation is that whenever you link to a dll and + // you import a function from it, the import should be tagged with + // `dllimport`. At this time, however, the compiler does not emit + // `dllimport` for any declarations other than constants (where it is + // required), which is again suboptimal for even more reasons! + // + // * Calling a function imported from another dll without using + // `dllimport` causes the linker/compiler to have extra overhead (one + // `jmp` instruction on x86) when calling the function. + // * The same object file may be used in different circumstances, so a + // function may be imported from a dll if the object is linked into a + // dll, but it may be just linked against if linked into an rlib. + // * The compiler has no knowledge about whether native functions should + // be tagged dllimport or not. + // + // For now the compiler takes the perf hit (I do not have any numbers to + // this effect) by marking very little as `dllimport` and praying the + // linker will take care of everything. Fixing this problem will likely + // require adding a few attributes to Rust itself (feature gated at the + // start) and then strongly recommending static linkage on MSVC! + let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; + + let check_overflow = tcx.sess.overflow_checks(); + + let tls_model = get_tls_model(&tcx.sess); + + unsafe { + let (llcx, llmod) = create_context_and_module(&tcx.sess, + &llmod_id[..]); + + let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { + let dctx = debuginfo::CrateDebugContext::new(llmod); + debuginfo::metadata::compile_unit_metadata(tcx, + &codegen_unit.name().as_str(), + &dctx); + Some(dctx) + } else { + None + }; + + let mut cx = CodegenCx { + tcx, + check_overflow, + use_dll_storage_attrs, + tls_model, + llmod, + llcx, + stats: RefCell::new(Stats::default()), + codegen_unit, + instances: RefCell::new(FxHashMap()), + vtables: RefCell::new(FxHashMap()), + const_cstr_cache: RefCell::new(FxHashMap()), + const_unsized: RefCell::new(FxHashMap()), + const_globals: RefCell::new(FxHashMap()), + statics: RefCell::new(FxHashMap()), + statics_to_rauw: RefCell::new(Vec::new()), + used_statics: RefCell::new(Vec::new()), + lltypes: RefCell::new(FxHashMap()), + scalar_lltypes: RefCell::new(FxHashMap()), + pointee_infos: RefCell::new(FxHashMap()), + isize_ty: Type::from_ref(ptr::null_mut()), + dbg_cx, + eh_personality: Cell::new(None), + eh_unwind_resume: Cell::new(None), + rust_try_fn: Cell::new(None), + intrinsics: RefCell::new(FxHashMap()), + local_gen_sym_counter: Cell::new(0), + }; + cx.isize_ty = Type::isize(&cx); + cx + } + } + + pub fn into_stats(self) -> Stats { + self.stats.into_inner() + } +} + +impl<'b, 'tcx> CodegenCx<'b, 'tcx> { + pub fn sess<'a>(&'a self) -> &'a Session { + &self.tcx.sess + } + + pub fn get_intrinsic(&self, key: &str) -> ValueRef { + if let Some(v) = self.intrinsics.borrow().get(key).cloned() { + return v; + } + match declare_intrinsic(self, key) { + Some(v) => return v, + None => bug!("unknown intrinsic '{}'", key) + } + } + + /// Generate a new symbol name with the given prefix. This symbol name must + /// only be used for definitions with `internal` or `private` linkage. + pub fn generate_local_symbol_name(&self, prefix: &str) -> String { + let idx = self.local_gen_sym_counter.get(); + self.local_gen_sym_counter.set(idx + 1); + // Include a '.' character, so there can be no accidental conflicts with + // user defined names + let mut name = String::with_capacity(prefix.len() + 6); + name.push_str(prefix); + name.push_str("."); + base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); + name + } + + pub fn eh_personality(&self) -> ValueRef { + // The exception handling personality function. + // + // If our compilation unit has the `eh_personality` lang item somewhere + // within it, then we just need to codegen that. Otherwise, we're + // building an rlib which will depend on some upstream implementation of + // this function, so we just codegen a generic reference to it. We don't + // specify any of the types for the function, we just make it a symbol + // that LLVM can later use. + // + // Note that MSVC is a little special here in that we don't use the + // `eh_personality` lang item at all. Currently LLVM has support for + // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the + // *name of the personality function* to decide what kind of unwind side + // tables/landing pads to emit. It looks like Dwarf is used by default, + // injecting a dependency on the `_Unwind_Resume` symbol for resuming + // an "exception", but for MSVC we want to force SEH. This means that we + // can't actually have the personality function be our standard + // `rust_eh_personality` function, but rather we wired it up to the + // CRT's custom personality function, which forces LLVM to consider + // landing pads as "landing pads for SEH". + if let Some(llpersonality) = self.eh_personality.get() { + return llpersonality + } + let tcx = self.tcx; + let llfn = match tcx.lang_items().eh_personality() { + Some(def_id) if !base::wants_msvc_seh(self.sess()) => { + callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) + } + _ => { + let name = if base::wants_msvc_seh(self.sess()) { + "__CxxFrameHandler3" + } else { + "rust_eh_personality" + }; + let fty = Type::variadic_func(&[], &Type::i32(self)); + declare::declare_cfn(self, name, fty) + } + }; + self.eh_personality.set(Some(llfn)); + llfn + } + + // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, + // otherwise declares it as an external function. + pub fn eh_unwind_resume(&self) -> ValueRef { + use attributes; + let unwresume = &self.eh_unwind_resume; + if let Some(llfn) = unwresume.get() { + return llfn; + } + + let tcx = self.tcx; + assert!(self.sess().target.target.options.custom_unwind_resume); + if let Some(def_id) = tcx.lang_items().eh_unwind_resume() { + let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); + unwresume.set(Some(llfn)); + return llfn; + } + + let ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + iter::once(tcx.mk_mut_ptr(tcx.types.u8)), + tcx.types.never, + false, + hir::Unsafety::Unsafe, + Abi::C + ))); + + let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); + attributes::unwind(llfn, true); + unwresume.set(Some(llfn)); + llfn + } + + pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { + common::type_needs_drop(self.tcx, ty) + } + + pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { + common::type_is_sized(self.tcx, ty) + } + + pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { + common::type_is_freeze(self.tcx, ty) + } + + pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { + use syntax_pos::DUMMY_SP; + if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { + return false; + } + + let tail = self.tcx.struct_tail(ty); + match tail.sty { + ty::TyForeign(..) => false, + ty::TyStr | ty::TySlice(..) | ty::TyDynamic(..) => true, + _ => bug!("unexpected unsized tail: {:?}", tail.sty), + } + } +} + +impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CodegenCx<'a, 'tcx> { + fn data_layout(&self) -> &ty::layout::TargetDataLayout { + &self.tcx.data_layout + } +} + +impl<'a, 'tcx> HasTargetSpec for &'a CodegenCx<'a, 'tcx> { + fn target_spec(&self) -> &Target { + &self.tcx.sess.target.target + } +} + +impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CodegenCx<'a, 'tcx> { + fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { + self.tcx + } +} + +impl<'a, 'tcx> LayoutOf for &'a CodegenCx<'a, 'tcx> { + type Ty = Ty<'tcx>; + type TyLayout = TyLayout<'tcx>; + + fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { + self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) + .unwrap_or_else(|e| match e { + LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), + _ => bug!("failed to get layout for `{}`: {}", ty, e) + }) + } +} + +/// Declare any llvm intrinsics that you might need +fn declare_intrinsic(cx: &CodegenCx, key: &str) -> Option { + macro_rules! ifn { + ($name:expr, fn() -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::func(&[], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, fn(...) -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::variadic_func(&[], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( + if key == $name { + let f = declare::declare_cfn(cx, $name, Type::func(&[$($arg),*], &$ret)); + llvm::SetUnnamedAddr(f, false); + cx.intrinsics.borrow_mut().insert($name, f.clone()); + return Some(f); + } + ); + } + macro_rules! mk_struct { + ($($field_ty:expr),*) => (Type::struct_(cx, &[$($field_ty),*], false)) + } + + let i8p = Type::i8p(cx); + let void = Type::void(cx); + let i1 = Type::i1(cx); + let t_i8 = Type::i8(cx); + let t_i16 = Type::i16(cx); + let t_i32 = Type::i32(cx); + let t_i64 = Type::i64(cx); + let t_i128 = Type::i128(cx); + let t_f32 = Type::f32(cx); + let t_f64 = Type::f64(cx); + + ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); + ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); + ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); + + ifn!("llvm.trap", fn() -> void); + ifn!("llvm.debugtrap", fn() -> void); + ifn!("llvm.frameaddress", fn(t_i32) -> i8p); + + ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); + ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); + ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); + + ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); + ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); + ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); + ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); + ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); + ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); + ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); + ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); + ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); + + ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); + ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); + ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); + ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); + ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); + ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); + ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); + ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); + ifn!("llvm.round.f32", fn(t_f32) -> t_f32); + ifn!("llvm.round.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); + ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); + ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); + + ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); + ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); + ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); + ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); + ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); + ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128); + + ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); + ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); + ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); + ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); + ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128); + + ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); + ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); + ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); + ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8); + ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16); + ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32); + ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); + ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); + + ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); + ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); + ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); + ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); + ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); + + ifn!("llvm.lifetime.start", fn(t_i64,i8p) -> void); + ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); + + ifn!("llvm.expect.i1", fn(i1, i1) -> i1); + ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); + ifn!("llvm.localescape", fn(...) -> void); + ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p); + ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p); + + ifn!("llvm.assume", fn(i1) -> void); + ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); + + if cx.sess().opts.debuginfo != NoDebugInfo { + ifn!("llvm.dbg.declare", fn(Type::metadata(cx), Type::metadata(cx)) -> void); + ifn!("llvm.dbg.value", fn(Type::metadata(cx), t_i64, Type::metadata(cx)) -> void); + } + return None; +} diff --git a/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs new file mode 100644 index 00000000000..bddb3d90940 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/create_scope_map.rs @@ -0,0 +1,136 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use super::{FunctionDebugContext, FunctionDebugContextData}; +use super::metadata::file_metadata; +use super::utils::{DIB, span_start}; + +use llvm; +use llvm::debuginfo::DIScope; +use common::CodegenCx; +use rustc::mir::{Mir, VisibilityScope}; + +use libc::c_uint; +use std::ptr; + +use syntax_pos::Pos; + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; + +use syntax_pos::BytePos; + +#[derive(Clone, Copy, Debug)] +pub struct MirDebugScope { + pub scope_metadata: DIScope, + // Start and end offsets of the file to which this DIScope belongs. + // These are used to quickly determine whether some span refers to the same file. + pub file_start_pos: BytePos, + pub file_end_pos: BytePos, +} + +impl MirDebugScope { + pub fn is_valid(&self) -> bool { + !self.scope_metadata.is_null() + } +} + +/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. +/// If debuginfo is disabled, the returned vector is empty. +pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebugContext) + -> IndexVec { + let null_scope = MirDebugScope { + scope_metadata: ptr::null_mut(), + file_start_pos: BytePos(0), + file_end_pos: BytePos(0) + }; + let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); + + let debug_context = match *debug_context { + FunctionDebugContext::RegularContext(ref data) => data, + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + return scopes; + } + }; + + // Find all the scopes with variables defined in them. + let mut has_variables = BitVector::new(mir.visibility_scopes.len()); + for var in mir.vars_iter() { + let decl = &mir.local_decls[var]; + has_variables.insert(decl.source_info.scope.index()); + } + + // Instantiate all scopes. + for idx in 0..mir.visibility_scopes.len() { + let scope = VisibilityScope::new(idx); + make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes); + } + + scopes +} + +fn make_mir_scope(cx: &CodegenCx, + mir: &Mir, + has_variables: &BitVector, + debug_context: &FunctionDebugContextData, + scope: VisibilityScope, + scopes: &mut IndexVec) { + if scopes[scope].is_valid() { + return; + } + + let scope_data = &mir.visibility_scopes[scope]; + let parent_scope = if let Some(parent) = scope_data.parent_scope { + make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes); + scopes[parent] + } else { + // The root is the function itself. + let loc = span_start(cx, mir.span); + scopes[scope] = MirDebugScope { + scope_metadata: debug_context.fn_metadata, + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + }; + return; + }; + + if !has_variables.contains(scope.index()) { + // Do not create a DIScope if there are no variables + // defined in this MIR Scope, to avoid debuginfo bloat. + + // However, we don't skip creating a nested scope if + // our parent is the root, because we might want to + // put arguments in the root and not have shadowing. + if parent_scope.scope_metadata != debug_context.fn_metadata { + scopes[scope] = parent_scope; + return; + } + } + + let loc = span_start(cx, scope_data.span); + let file_metadata = file_metadata(cx, + &loc.file.name, + debug_context.defining_crate); + + let scope_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlock( + DIB(cx), + parent_scope.scope_metadata, + file_metadata, + loc.line as c_uint, + loc.col.to_usize() as c_uint) + }; + scopes[scope] = MirDebugScope { + scope_metadata, + file_start_pos: loc.file.start_pos, + file_end_pos: loc.file.end_pos, + }; +} diff --git a/src/librustc_codegen_llvm/debuginfo/doc.rs b/src/librustc_codegen_llvm/debuginfo/doc.rs new file mode 100644 index 00000000000..ce0476b07eb --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/doc.rs @@ -0,0 +1,189 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! # Debug Info Module +//! +//! This module serves the purpose of generating debug symbols. We use LLVM's +//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html) +//! features for generating the debug information. The general principle is +//! this: +//! +//! Given the right metadata in the LLVM IR, the LLVM code generator is able to +//! create DWARF debug symbols for the given code. The +//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured +//! much like DWARF *debugging information entries* (DIE), representing type +//! information such as datatype layout, function signatures, block layout, +//! variable location and scope information, etc. It is the purpose of this +//! module to generate correct metadata and insert it into the LLVM IR. +//! +//! As the exact format of metadata trees may change between different LLVM +//! versions, we now use LLVM +//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) +//! to create metadata where possible. This will hopefully ease the adaption of +//! this module to future LLVM versions. +//! +//! The public API of the module is a set of functions that will insert the +//! correct metadata into the LLVM IR when called with the right parameters. +//! The module is thus driven from an outside client with functions like +//! `debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. +//! +//! Internally the module will try to reuse already created metadata by +//! utilizing a cache. The way to get a shared metadata node when needed is +//! thus to just call the corresponding function in this module: +//! +//! let file_metadata = file_metadata(crate_context, path); +//! +//! The function will take care of probing the cache for an existing node for +//! that exact file path. +//! +//! All private state used by the module is stored within either the +//! CrateDebugContext struct (owned by the CodegenCx) or the +//! FunctionDebugContext (owned by the FunctionCx). +//! +//! This file consists of three conceptual sections: +//! 1. The public interface of the module +//! 2. Module-internal metadata creation functions +//! 3. Minor utility functions +//! +//! +//! ## Recursive Types +//! +//! Some kinds of types, such as structs and enums can be recursive. That means +//! that the type definition of some type X refers to some other type which in +//! turn (transitively) refers to X. This introduces cycles into the type +//! referral graph. A naive algorithm doing an on-demand, depth-first traversal +//! of this graph when describing types, can get trapped in an endless loop +//! when it reaches such a cycle. +//! +//! For example, the following simple type for a singly-linked list... +//! +//! ``` +//! struct List { +//! value: i32, +//! tail: Option>, +//! } +//! ``` +//! +//! will generate the following callstack with a naive DFS algorithm: +//! +//! ``` +//! describe(t = List) +//! describe(t = i32) +//! describe(t = Option>) +//! describe(t = Box) +//! describe(t = List) // at the beginning again... +//! ... +//! ``` +//! +//! To break cycles like these, we use "forward declarations". That is, when +//! the algorithm encounters a possibly recursive type (any struct or enum), it +//! immediately creates a type description node and inserts it into the cache +//! *before* describing the members of the type. This type description is just +//! a stub (as type members are not described and added to it yet) but it +//! allows the algorithm to already refer to the type. After the stub is +//! inserted into the cache, the algorithm continues as before. If it now +//! encounters a recursive reference, it will hit the cache and does not try to +//! describe the type anew. +//! +//! This behavior is encapsulated in the 'RecursiveTypeDescription' enum, +//! which represents a kind of continuation, storing all state needed to +//! continue traversal at the type members after the type has been registered +//! with the cache. (This implementation approach might be a tad over- +//! engineered and may change in the future) +//! +//! +//! ## Source Locations and Line Information +//! +//! In addition to data type descriptions the debugging information must also +//! allow to map machine code locations back to source code locations in order +//! to be useful. This functionality is also handled in this module. The +//! following functions allow to control source mappings: +//! +//! + set_source_location() +//! + clear_source_location() +//! + start_emitting_source_locations() +//! +//! `set_source_location()` allows to set the current source location. All IR +//! instructions created after a call to this function will be linked to the +//! given source location, until another location is specified with +//! `set_source_location()` or the source location is cleared with +//! `clear_source_location()`. In the later case, subsequent IR instruction +//! will not be linked to any source location. As you can see, this is a +//! stateful API (mimicking the one in LLVM), so be careful with source +//! locations set by previous calls. It's probably best to not rely on any +//! specific state being present at a given point in code. +//! +//! One topic that deserves some extra attention is *function prologues*. At +//! the beginning of a function's machine code there are typically a few +//! instructions for loading argument values into allocas and checking if +//! there's enough stack space for the function to execute. This *prologue* is +//! not visible in the source code and LLVM puts a special PROLOGUE END marker +//! into the line table at the first non-prologue instruction of the function. +//! In order to find out where the prologue ends, LLVM looks for the first +//! instruction in the function body that is linked to a source location. So, +//! when generating prologue instructions we have to make sure that we don't +//! emit source location information until the 'real' function body begins. For +//! this reason, source location emission is disabled by default for any new +//! function being codegened and is only activated after a call to the third +//! function from the list above, `start_emitting_source_locations()`. This +//! function should be called right before regularly starting to codegen the +//! top-level block of the given function. +//! +//! There is one exception to the above rule: `llvm.dbg.declare` instruction +//! must be linked to the source location of the variable being declared. For +//! function parameters these `llvm.dbg.declare` instructions typically occur +//! in the middle of the prologue, however, they are ignored by LLVM's prologue +//! detection. The `create_argument_metadata()` and related functions take care +//! of linking the `llvm.dbg.declare` instructions to the correct source +//! locations even while source location emission is still disabled, so there +//! is no need to do anything special with source location handling here. +//! +//! ## Unique Type Identification +//! +//! In order for link-time optimization to work properly, LLVM needs a unique +//! type identifier that tells it across compilation units which types are the +//! same as others. This type identifier is created by +//! TypeMap::get_unique_type_id_of_type() using the following algorithm: +//! +//! (1) Primitive types have their name as ID +//! (2) Structs, enums and traits have a multipart identifier +//! +//! (1) The first part is the SVH (strict version hash) of the crate they +//! were originally defined in +//! +//! (2) The second part is the ast::NodeId of the definition in their +//! original crate +//! +//! (3) The final part is a concatenation of the type IDs of their concrete +//! type arguments if they are generic types. +//! +//! (3) Tuple-, pointer and function types are structurally identified, which +//! means that they are equivalent if their component types are equivalent +//! (i.e. (i32, i32) is the same regardless in which crate it is used). +//! +//! This algorithm also provides a stable ID for types that are defined in one +//! crate but instantiated from metadata within another crate. We just have to +//! take care to always map crate and node IDs back to the original crate +//! context. +//! +//! As a side-effect these unique type IDs also help to solve a problem arising +//! from lifetime parameters. Since lifetime parameters are completely omitted +//! in debuginfo, more than one `Ty` instance may map to the same debuginfo +//! type metadata, that is, some struct `Struct<'a>` may have N instantiations +//! with different concrete substitutions for `'a`, and thus there will be N +//! `Ty` instances for the type `Struct<'a>` even though it is not generic +//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as +//! cheap identifier for type metadata---we have done this in the past, but it +//! led to unnecessary metadata duplication in the best case and LLVM +//! assertions in the worst. However, the unique type ID as described above +//! *can* be used as identifier. Since it is comparatively expensive to +//! construct, though, `ty::type_id()` is still used additionally as an +//! optimization for cases where the exact same type has been seen before +//! (which is most of the time). diff --git a/src/librustc_codegen_llvm/debuginfo/gdb.rs b/src/librustc_codegen_llvm/debuginfo/gdb.rs new file mode 100644 index 00000000000..0b4858c7ab0 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/gdb.rs @@ -0,0 +1,88 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// .debug_gdb_scripts binary section. + +use llvm; + +use common::{C_bytes, CodegenCx, C_i32}; +use builder::Builder; +use declare; +use type_::Type; +use rustc::session::config::NoDebugInfo; + +use std::ptr; +use syntax::attr; + + +/// Inserts a side-effect free instruction sequence that makes sure that the +/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. +pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &Builder) { + if needs_gdb_debug_scripts_section(bx.cx) { + let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx.cx); + // Load just the first byte as that's all that's necessary to force + // LLVM to keep around the reference to the global. + let indices = [C_i32(bx.cx, 0), C_i32(bx.cx, 0)]; + let element = bx.inbounds_gep(gdb_debug_scripts_section, &indices); + let volative_load_instruction = bx.volatile_load(element); + unsafe { + llvm::LLVMSetAlignment(volative_load_instruction, 1); + } + } +} + +/// Allocates the global variable responsible for the .debug_gdb_scripts binary +/// section. +pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx) + -> llvm::ValueRef { + let c_section_var_name = "__rustc_debug_gdb_scripts_section__\0"; + let section_var_name = &c_section_var_name[..c_section_var_name.len()-1]; + + let section_var = unsafe { + llvm::LLVMGetNamedGlobal(cx.llmod, + c_section_var_name.as_ptr() as *const _) + }; + + if section_var == ptr::null_mut() { + let section_name = b".debug_gdb_scripts\0"; + let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; + + unsafe { + let llvm_type = Type::array(&Type::i8(cx), + section_contents.len() as u64); + + let section_var = declare::define_global(cx, section_var_name, + llvm_type).unwrap_or_else(||{ + bug!("symbol `{}` is already defined", section_var_name) + }); + llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); + llvm::LLVMSetInitializer(section_var, C_bytes(cx, section_contents)); + llvm::LLVMSetGlobalConstant(section_var, llvm::True); + llvm::LLVMSetUnnamedAddr(section_var, llvm::True); + llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); + // This should make sure that the whole section is not larger than + // the string it contains. Otherwise we get a warning from GDB. + llvm::LLVMSetAlignment(section_var, 1); + section_var + } + } else { + section_var + } +} + +pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx) -> bool { + let omit_gdb_pretty_printer_section = + attr::contains_name(&cx.tcx.hir.krate_attrs(), + "omit_gdb_pretty_printer_section"); + + !omit_gdb_pretty_printer_section && + cx.sess().opts.debuginfo != NoDebugInfo && + cx.sess().target.target.options.emit_debug_gdb_scripts +} diff --git a/src/librustc_codegen_llvm/debuginfo/metadata.rs b/src/librustc_codegen_llvm/debuginfo/metadata.rs new file mode 100644 index 00000000000..ee60711c11d --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/metadata.rs @@ -0,0 +1,1773 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::RecursiveTypeDescription::*; +use self::MemberDescriptionFactory::*; +use self::EnumDiscriminantInfo::*; + +use super::utils::{debug_context, DIB, span_start, + get_namespace_for_item, create_DIArray, is_node_local_to_unit}; +use super::namespace::mangled_name_of_instance; +use super::type_names::compute_debuginfo_type_name; +use super::{CrateDebugContext}; +use abi; + +use llvm::{self, ValueRef}; +use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, + DICompositeType, DILexicalBlock, DIFlags}; + +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def::CtorKind; +use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; +use rustc::ty::fold::TypeVisitor; +use rustc::ty::util::TypeIdHasher; +use rustc::ich::Fingerprint; +use rustc::ty::Instance; +use common::CodegenCx; +use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; +use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; +use rustc::session::config; +use rustc::util::nodemap::FxHashMap; +use rustc::util::common::path2cstr; + +use libc::{c_uint, c_longlong}; +use std::ffi::CString; +use std::fmt::Write; +use std::ptr; +use std::path::{Path, PathBuf}; +use syntax::ast; +use syntax::symbol::{Interner, InternedString, Symbol}; +use syntax_pos::{self, Span, FileName}; + + +// From DWARF 5. +// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1 +const DW_LANG_RUST: c_uint = 0x1c; +#[allow(non_upper_case_globals)] +const DW_ATE_boolean: c_uint = 0x02; +#[allow(non_upper_case_globals)] +const DW_ATE_float: c_uint = 0x04; +#[allow(non_upper_case_globals)] +const DW_ATE_signed: c_uint = 0x05; +#[allow(non_upper_case_globals)] +const DW_ATE_unsigned: c_uint = 0x07; +#[allow(non_upper_case_globals)] +const DW_ATE_unsigned_char: c_uint = 0x08; + +pub const UNKNOWN_LINE_NUMBER: c_uint = 0; +pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; + +// ptr::null() doesn't work :( +pub const NO_SCOPE_METADATA: DIScope = (0 as DIScope); + +#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] +pub struct UniqueTypeId(ast::Name); + +// The TypeMap is where the CrateDebugContext holds the type metadata nodes +// created so far. The metadata nodes are indexed by UniqueTypeId, and, for +// faster lookup, also by Ty. The TypeMap is responsible for creating +// UniqueTypeIds. +pub struct TypeMap<'tcx> { + // The UniqueTypeIds created so far + unique_id_interner: Interner, + // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. + unique_id_to_metadata: FxHashMap, + // A map from types to debuginfo metadata. This is a N:1 mapping. + type_to_metadata: FxHashMap, DIType>, + // A map from types to UniqueTypeId. This is a N:1 mapping. + type_to_unique_id: FxHashMap, UniqueTypeId> +} + +impl<'tcx> TypeMap<'tcx> { + pub fn new() -> TypeMap<'tcx> { + TypeMap { + unique_id_interner: Interner::new(), + type_to_metadata: FxHashMap(), + unique_id_to_metadata: FxHashMap(), + type_to_unique_id: FxHashMap(), + } + } + + // Adds a Ty to metadata mapping to the TypeMap. The method will fail if + // the mapping already exists. + fn register_type_with_metadata<'a>(&mut self, + type_: Ty<'tcx>, + metadata: DIType) { + if self.type_to_metadata.insert(type_, metadata).is_some() { + bug!("Type metadata for Ty '{}' is already in the TypeMap!", type_); + } + } + + // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will + // fail if the mapping already exists. + fn register_unique_id_with_metadata(&mut self, + unique_type_id: UniqueTypeId, + metadata: DIType) { + if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { + bug!("Type metadata for unique id '{}' is already in the TypeMap!", + self.get_unique_type_id_as_string(unique_type_id)); + } + } + + fn find_metadata_for_type(&self, type_: Ty<'tcx>) -> Option { + self.type_to_metadata.get(&type_).cloned() + } + + fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { + self.unique_id_to_metadata.get(&unique_type_id).cloned() + } + + // Get the string representation of a UniqueTypeId. This method will fail if + // the id is unknown. + fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> &str { + let UniqueTypeId(interner_key) = unique_type_id; + self.unique_id_interner.get(interner_key) + } + + // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given + // type has been requested before, this is just a table lookup. Otherwise an + // ID will be generated and stored for later lookup. + fn get_unique_type_id_of_type<'a>(&mut self, cx: &CodegenCx<'a, 'tcx>, + type_: Ty<'tcx>) -> UniqueTypeId { + // Let's see if we already have something in the cache + match self.type_to_unique_id.get(&type_).cloned() { + Some(unique_type_id) => return unique_type_id, + None => { /* generate one */} + }; + + // The hasher we are using to generate the UniqueTypeId. We want + // something that provides more than the 64 bits of the DefaultHasher. + let mut type_id_hasher = TypeIdHasher::::new(cx.tcx); + type_id_hasher.visit_ty(type_); + let unique_type_id = type_id_hasher.finish().to_hex(); + + let key = self.unique_id_interner.intern(&unique_type_id); + self.type_to_unique_id.insert(type_, UniqueTypeId(key)); + + return UniqueTypeId(key); + } + + // Get the UniqueTypeId for an enum variant. Enum variants are not really + // types of their own, so they need special handling. We still need a + // UniqueTypeId for them, since to debuginfo they *are* real types. + fn get_unique_type_id_of_enum_variant<'a>(&mut self, + cx: &CodegenCx<'a, 'tcx>, + enum_type: Ty<'tcx>, + variant_name: &str) + -> UniqueTypeId { + let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); + let enum_variant_type_id = format!("{}::{}", + self.get_unique_type_id_as_string(enum_type_id), + variant_name); + let interner_key = self.unique_id_interner.intern(&enum_variant_type_id); + UniqueTypeId(interner_key) + } +} + +// A description of some recursive type. It can either be already finished (as +// with FinalMetadata) or it is not yet finished, but contains all information +// needed to generate the missing parts of the description. See the +// documentation section on Recursive Types at the top of this file for more +// information. +enum RecursiveTypeDescription<'tcx> { + UnfinishedMetadata { + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + member_description_factory: MemberDescriptionFactory<'tcx>, + }, + FinalMetadata(DICompositeType) +} + +fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + unfinished_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + metadata_stub: DICompositeType, + member_description_factory: MemberDescriptionFactory<'tcx>) + -> RecursiveTypeDescription<'tcx> { + + // Insert the stub into the TypeMap in order to allow for recursive references + let mut type_map = debug_context(cx).type_map.borrow_mut(); + type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub); + type_map.register_type_with_metadata(unfinished_type, metadata_stub); + + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + member_description_factory, + } +} + +impl<'tcx> RecursiveTypeDescription<'tcx> { + // Finishes up the description of the type in question (mostly by providing + // descriptions of the fields of the given type) and returns the final type + // metadata. + fn finalize<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> MetadataCreationResult { + match *self { + FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), + UnfinishedMetadata { + unfinished_type, + unique_type_id, + metadata_stub, + ref member_description_factory, + } => { + // Make sure that we have a forward declaration of the type in + // the TypeMap so that recursive references are possible. This + // will always be the case if the RecursiveTypeDescription has + // been properly created through the + // create_and_register_recursive_type_forward_declaration() + // function. + { + let type_map = debug_context(cx).type_map.borrow(); + if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || + type_map.find_metadata_for_type(unfinished_type).is_none() { + bug!("Forward declaration of potentially recursive type \ + '{:?}' was not found in TypeMap!", + unfinished_type); + } + } + + // ... then create the member descriptions ... + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + // ... and attach them to the stub to complete it. + set_members_of_composite_type(cx, + metadata_stub, + &member_descriptions[..]); + return MetadataCreationResult::new(metadata_stub, true); + } + } + } +} + +// Returns from the enclosing function if the type metadata with the given +// unique id can be found in the type map +macro_rules! return_if_metadata_created_in_meantime { + ($cx: expr, $unique_type_id: expr) => ( + match debug_context($cx).type_map + .borrow() + .find_metadata_for_unique_id($unique_type_id) { + Some(metadata) => return MetadataCreationResult::new(metadata, true), + None => { /* proceed normally */ } + } + ) +} + +fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + unique_type_id: UniqueTypeId, + array_or_slice_type: Ty<'tcx>, + element_type: Ty<'tcx>, + span: Span) + -> MetadataCreationResult { + let element_type_metadata = type_metadata(cx, element_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let (size, align) = cx.size_and_align_of(array_or_slice_type); + + let upper_bound = match array_or_slice_type.sty { + ty::TyArray(_, len) => { + len.unwrap_usize(cx.tcx) as c_longlong + } + _ => -1 + }; + + let subrange = unsafe { + llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) + }; + + let subscripts = create_DIArray(DIB(cx), &[subrange]); + let metadata = unsafe { + llvm::LLVMRustDIBuilderCreateArrayType( + DIB(cx), + size.bits(), + align.abi_bits() as u32, + element_type_metadata, + subscripts) + }; + + return MetadataCreationResult::new(metadata, false); +} + +fn vec_slice_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + slice_ptr_type: Ty<'tcx>, + element_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> MetadataCreationResult { + let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); + + let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + let slice_type_name = compute_debuginfo_type_name(cx, slice_ptr_type, true); + + let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); + let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); + + let member_descriptions = [ + MemberDescription { + name: "data_ptr".to_string(), + type_metadata: data_ptr_metadata, + offset: Size::from_bytes(0), + size: pointer_size, + align: pointer_align, + flags: DIFlags::FlagZero, + }, + MemberDescription { + name: "length".to_string(), + type_metadata: type_metadata(cx, cx.tcx.types.usize, span), + offset: pointer_size, + size: usize_size, + align: usize_align, + flags: DIFlags::FlagZero, + }, + ]; + + let file_metadata = unknown_file_metadata(cx); + + let metadata = composite_type_metadata(cx, + slice_ptr_type, + &slice_type_name[..], + unique_type_id, + &member_descriptions, + NO_SCOPE_METADATA, + file_metadata, + span); + MetadataCreationResult::new(metadata, false) +} + +fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + unique_type_id: UniqueTypeId, + signature: ty::PolyFnSig<'tcx>, + span: Span) + -> MetadataCreationResult +{ + let signature = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &signature, + ); + + let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs().len() + 1); + + // return type + signature_metadata.push(match signature.output().sty { + ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, signature.output(), span) + }); + + // regular arguments + for &argument_type in signature.inputs() { + signature_metadata.push(type_metadata(cx, argument_type, span)); + } + + return_if_metadata_created_in_meantime!(cx, unique_type_id); + + return MetadataCreationResult::new( + unsafe { + llvm::LLVMRustDIBuilderCreateSubroutineType( + DIB(cx), + unknown_file_metadata(cx), + create_DIArray(DIB(cx), &signature_metadata[..])) + }, + false); +} + +// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- +// defined concept. For the case of an actual trait pointer (i.e., Box, +// &Trait), trait_object_type should be the whole thing (e.g, Box) and +// trait_type should be the actual trait (e.g., Trait). Where the trait is part +// of a DST struct, there is no trait_object_type and the results of this +// function will be a little bit weird. +fn trait_pointer_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + trait_type: Ty<'tcx>, + trait_object_type: Option>, + unique_type_id: UniqueTypeId) + -> DIType { + // The implementation provided here is a stub. It makes sure that the trait + // type is assigned the correct name, size, namespace, and source location. + // But it does not describe the trait's methods. + + let containing_scope = match trait_type.sty { + ty::TyDynamic(ref data, ..) => if let Some(principal) = data.principal() { + let def_id = principal.def_id(); + get_namespace_for_item(cx, def_id) + } else { + NO_SCOPE_METADATA + }, + _ => { + bug!("debuginfo: Unexpected trait-object type in \ + trait_pointer_metadata(): {:?}", + trait_type); + } + }; + + let trait_object_type = trait_object_type.unwrap_or(trait_type); + let trait_type_name = + compute_debuginfo_type_name(cx, trait_object_type, false); + + let file_metadata = unknown_file_metadata(cx); + + let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); + + assert_eq!(abi::FAT_PTR_ADDR, 0); + assert_eq!(abi::FAT_PTR_EXTRA, 1); + + let data_ptr_field = layout.field(cx, 0); + let vtable_field = layout.field(cx, 1); + let member_descriptions = [ + MemberDescription { + name: "pointer".to_string(), + type_metadata: type_metadata(cx, + cx.tcx.mk_mut_ptr(cx.tcx.types.u8), + syntax_pos::DUMMY_SP), + offset: layout.fields.offset(0), + size: data_ptr_field.size, + align: data_ptr_field.align, + flags: DIFlags::FlagArtificial, + }, + MemberDescription { + name: "vtable".to_string(), + type_metadata: type_metadata(cx, vtable_field.ty, syntax_pos::DUMMY_SP), + offset: layout.fields.offset(1), + size: vtable_field.size, + align: vtable_field.align, + flags: DIFlags::FlagArtificial, + }, + ]; + + composite_type_metadata(cx, + trait_object_type, + &trait_type_name[..], + unique_type_id, + &member_descriptions, + containing_scope, + file_metadata, + syntax_pos::DUMMY_SP) +} + +pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + usage_site_span: Span) + -> DIType { + // Get the unique type id of this type. + let unique_type_id = { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + // First, try to find the type in TypeMap. If we have seen it before, we + // can exit early here. + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + return metadata; + }, + None => { + // The Ty is not in the TypeMap but maybe we have already seen + // an equivalent type (e.g. only differing in region arguments). + // In order to find out, generate the unique type id and look + // that up. + let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); + match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => { + // There is already an equivalent type in the TypeMap. + // Register this Ty as an alias in the cache and + // return the cached metadata. + type_map.register_type_with_metadata(t, metadata); + return metadata; + }, + None => { + // There really is no type metadata for this type, so + // proceed by creating it. + unique_type_id + } + } + } + } + }; + + debug!("type_metadata: {:?}", t); + + let ptr_metadata = |ty: Ty<'tcx>| { + match ty.sty { + ty::TySlice(typ) => { + Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)) + } + ty::TyStr => { + Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)) + } + ty::TyDynamic(..) => { + Ok(MetadataCreationResult::new( + trait_pointer_metadata(cx, ty, Some(t), unique_type_id), + false)) + } + _ => { + let pointee_metadata = type_metadata(cx, ty, usage_site_span); + + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return Err(metadata), + None => { /* proceed normally */ } + }; + + Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), + false)) + } + } + }; + + let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.sty { + ty::TyNever | + ty::TyBool | + ty::TyChar | + ty::TyInt(_) | + ty::TyUint(_) | + ty::TyFloat(_) => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::TyTuple(ref elements) if elements.is_empty() => { + MetadataCreationResult::new(basic_type_metadata(cx, t), false) + } + ty::TyArray(typ, _) | + ty::TySlice(typ) => { + fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) + } + ty::TyStr => { + fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8, usage_site_span) + } + ty::TyDynamic(..) => { + MetadataCreationResult::new( + trait_pointer_metadata(cx, t, None, unique_type_id), + false) + } + ty::TyForeign(..) => { + MetadataCreationResult::new( + foreign_type_metadata(cx, t, unique_type_id), + false) + } + ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | + ty::TyRef(_, ty, _) => { + match ptr_metadata(ty) { + Ok(res) => res, + Err(metadata) => return metadata, + } + } + ty::TyAdt(def, _) if def.is_box() => { + match ptr_metadata(t.boxed_ty()) { + Ok(res) => res, + Err(metadata) => return metadata, + } + } + ty::TyFnDef(..) | ty::TyFnPtr(_) => { + let fn_metadata = subroutine_type_metadata(cx, + unique_type_id, + t.fn_sig(cx.tcx), + usage_site_span).metadata; + match debug_context(cx).type_map + .borrow() + .find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => return metadata, + None => { /* proceed normally */ } + }; + + // This is actually a function pointer, so wrap it in pointer DI + MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) + + } + ty::TyClosure(def_id, substs) => { + let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); + prepare_tuple_metadata(cx, + t, + &upvar_tys, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::TyGenerator(def_id, substs, _) => { + let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| { + cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) + }).collect(); + prepare_tuple_metadata(cx, + t, + &upvar_tys, + unique_type_id, + usage_site_span).finalize(cx) + } + ty::TyAdt(def, ..) => match def.adt_kind() { + AdtKind::Struct => { + prepare_struct_metadata(cx, + t, + unique_type_id, + usage_site_span).finalize(cx) + } + AdtKind::Union => { + prepare_union_metadata(cx, + t, + unique_type_id, + usage_site_span).finalize(cx) + } + AdtKind::Enum => { + prepare_enum_metadata(cx, + t, + def.did, + unique_type_id, + usage_site_span).finalize(cx) + } + }, + ty::TyTuple(ref elements) => { + prepare_tuple_metadata(cx, + t, + &elements[..], + unique_type_id, + usage_site_span).finalize(cx) + } + _ => { + bug!("debuginfo: unexpected type in type_metadata: {:?}", t) + } + }; + + { + let mut type_map = debug_context(cx).type_map.borrow_mut(); + + if already_stored_in_typemap { + // Also make sure that we already have a TypeMap entry for the unique type id. + let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { + Some(metadata) => metadata, + None => { + span_bug!(usage_site_span, + "Expected type metadata for unique \ + type id '{}' to already be in \ + the debuginfo::TypeMap but it \ + was not. (Ty = {})", + type_map.get_unique_type_id_as_string(unique_type_id), + t); + } + }; + + match type_map.find_metadata_for_type(t) { + Some(metadata) => { + if metadata != metadata_for_uid { + span_bug!(usage_site_span, + "Mismatch between Ty and \ + UniqueTypeId maps in \ + debuginfo::TypeMap. \ + UniqueTypeId={}, Ty={}", + type_map.get_unique_type_id_as_string(unique_type_id), + t); + } + } + None => { + type_map.register_type_with_metadata(t, metadata); + } + } + } else { + type_map.register_type_with_metadata(t, metadata); + type_map.register_unique_id_with_metadata(unique_type_id, metadata); + } + } + + metadata +} + +pub fn file_metadata(cx: &CodegenCx, + file_name: &FileName, + defining_crate: CrateNum) -> DIFile { + debug!("file_metadata: file_name: {}, defining_crate: {}", + file_name, + defining_crate); + + let directory = if defining_crate == LOCAL_CRATE { + &cx.sess().working_dir.0 + } else { + // If the path comes from an upstream crate we assume it has been made + // independent of the compiler's working directory one way or another. + Path::new("") + }; + + file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy()) +} + +pub fn unknown_file_metadata(cx: &CodegenCx) -> DIFile { + file_metadata_raw(cx, "", "") +} + +fn file_metadata_raw(cx: &CodegenCx, + file_name: &str, + directory: &str) + -> DIFile { + let key = (Symbol::intern(file_name), Symbol::intern(directory)); + + if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) { + return *file_metadata; + } + + debug!("file_metadata: file_name: {}, directory: {}", file_name, directory); + + let file_name = CString::new(file_name).unwrap(); + let directory = CString::new(directory).unwrap(); + + let file_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateFile(DIB(cx), + file_name.as_ptr(), + directory.as_ptr()) + }; + + let mut created_files = debug_context(cx).created_files.borrow_mut(); + created_files.insert(key, file_metadata); + file_metadata +} + +fn basic_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>) -> DIType { + + debug!("basic_type_metadata: {:?}", t); + + let (name, encoding) = match t.sty { + ty::TyNever => ("!", DW_ATE_unsigned), + ty::TyTuple(ref elements) if elements.is_empty() => + ("()", DW_ATE_unsigned), + ty::TyBool => ("bool", DW_ATE_boolean), + ty::TyChar => ("char", DW_ATE_unsigned_char), + ty::TyInt(int_ty) => { + (int_ty.ty_to_string(), DW_ATE_signed) + }, + ty::TyUint(uint_ty) => { + (uint_ty.ty_to_string(), DW_ATE_unsigned) + }, + ty::TyFloat(float_ty) => { + (float_ty.ty_to_string(), DW_ATE_float) + }, + _ => bug!("debuginfo::basic_type_metadata - t is invalid type") + }; + + let (size, align) = cx.size_and_align_of(t); + let name = CString::new(name).unwrap(); + let ty_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateBasicType( + DIB(cx), + name.as_ptr(), + size.bits(), + align.abi_bits() as u32, + encoding) + }; + + return ty_metadata; +} + +fn foreign_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + unique_type_id: UniqueTypeId) -> DIType { + debug!("foreign_type_metadata: {:?}", t); + + let name = compute_debuginfo_type_name(cx, t, false); + create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA) +} + +fn pointer_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + pointer_type: Ty<'tcx>, + pointee_type_metadata: DIType) + -> DIType { + let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); + let name = compute_debuginfo_type_name(cx, pointer_type, false); + let name = CString::new(name).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreatePointerType( + DIB(cx), + pointee_type_metadata, + pointer_size.bits(), + pointer_align.abi_bits() as u32, + name.as_ptr()) + } +} + +pub fn compile_unit_metadata(tcx: TyCtxt, + codegen_unit_name: &str, + debug_context: &CrateDebugContext) + -> DIDescriptor { + let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { + Some(ref path) => path.clone(), + None => PathBuf::from(&*tcx.crate_name(LOCAL_CRATE).as_str()), + }; + + // The OSX linker has an idiosyncrasy where it will ignore some debuginfo + // if multiple object files with the same DW_AT_name are linked together. + // As a workaround we generate unique names for each object file. Those do + // not correspond to an actual source file but that should be harmless. + if tcx.sess.target.target.options.is_like_osx { + name_in_debuginfo.push("@"); + name_in_debuginfo.push(codegen_unit_name); + } + + debug!("compile_unit_metadata: {:?}", name_in_debuginfo); + // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. + let producer = format!("clang LLVM (rustc version {})", + (option_env!("CFG_VERSION")).expect("CFG_VERSION")); + + let name_in_debuginfo = name_in_debuginfo.to_string_lossy().into_owned(); + let name_in_debuginfo = CString::new(name_in_debuginfo).unwrap(); + let work_dir = CString::new(&tcx.sess.working_dir.0.to_string_lossy()[..]).unwrap(); + let producer = CString::new(producer).unwrap(); + let flags = "\0"; + let split_name = "\0"; + + unsafe { + let file_metadata = llvm::LLVMRustDIBuilderCreateFile( + debug_context.builder, name_in_debuginfo.as_ptr(), work_dir.as_ptr()); + + let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( + debug_context.builder, + DW_LANG_RUST, + file_metadata, + producer.as_ptr(), + tcx.sess.opts.optimize != config::OptLevel::No, + flags.as_ptr() as *const _, + 0, + split_name.as_ptr() as *const _); + + if tcx.sess.opts.debugging_opts.profile { + let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, + unit_metadata); + + let gcov_cu_info = [ + path_to_mdstring(debug_context.llcontext, + &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno")), + path_to_mdstring(debug_context.llcontext, + &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda")), + cu_desc_metadata, + ]; + let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext, + gcov_cu_info.as_ptr(), + gcov_cu_info.len() as c_uint); + + let llvm_gcov_ident = CString::new("llvm.gcov").unwrap(); + llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, + llvm_gcov_ident.as_ptr(), + gcov_metadata); + } + + return unit_metadata; + }; + + fn path_to_mdstring(llcx: llvm::ContextRef, path: &Path) -> llvm::ValueRef { + let path_str = path2cstr(path); + unsafe { + llvm::LLVMMDStringInContext(llcx, + path_str.as_ptr(), + path_str.as_bytes().len() as c_uint) + } + } +} + +struct MetadataCreationResult { + metadata: DIType, + already_stored_in_typemap: bool +} + +impl MetadataCreationResult { + fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { + MetadataCreationResult { + metadata, + already_stored_in_typemap, + } + } +} + +// Description of a type member, which can either be a regular field (as in +// structs or tuples) or an enum variant. +#[derive(Debug)] +struct MemberDescription { + name: String, + type_metadata: DIType, + offset: Size, + size: Size, + align: Align, + flags: DIFlags, +} + +// A factory for MemberDescriptions. It produces a list of member descriptions +// for some record-like type. MemberDescriptionFactories are used to defer the +// creation of type member descriptions in order to break cycles arising from +// recursive type definitions. +enum MemberDescriptionFactory<'tcx> { + StructMDF(StructMemberDescriptionFactory<'tcx>), + TupleMDF(TupleMemberDescriptionFactory<'tcx>), + EnumMDF(EnumMemberDescriptionFactory<'tcx>), + UnionMDF(UnionMemberDescriptionFactory<'tcx>), + VariantMDF(VariantMemberDescriptionFactory<'tcx>) +} + +impl<'tcx> MemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + match *self { + StructMDF(ref this) => { + this.create_member_descriptions(cx) + } + TupleMDF(ref this) => { + this.create_member_descriptions(cx) + } + EnumMDF(ref this) => { + this.create_member_descriptions(cx) + } + UnionMDF(ref this) => { + this.create_member_descriptions(cx) + } + VariantMDF(ref this) => { + this.create_member_descriptions(cx) + } + } + } +} + +//=----------------------------------------------------------------------------- +// Structs +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a struct +struct StructMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, + variant: &'tcx ty::VariantDef, + span: Span, +} + +impl<'tcx> StructMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let layout = cx.layout_of(self.ty); + self.variant.fields.iter().enumerate().map(|(i, f)| { + let name = if self.variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + f.name.to_string() + }; + let field = layout.field(cx, i); + let (size, align) = field.size_and_align(); + MemberDescription { + name, + type_metadata: type_metadata(cx, field.ty, self.span), + offset: layout.fields.offset(i), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + + +fn prepare_struct_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + struct_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let struct_name = compute_debuginfo_type_name(cx, struct_type, false); + + let (struct_def_id, variant) = match struct_type.sty { + ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), + _ => bug!("prepare_struct_metadata on a non-ADT") + }; + + let containing_scope = get_namespace_for_item(cx, struct_def_id); + + let struct_metadata_stub = create_struct_stub(cx, + struct_type, + &struct_name, + unique_type_id, + containing_scope); + + create_and_register_recursive_type_forward_declaration( + cx, + struct_type, + unique_type_id, + struct_metadata_stub, + StructMDF(StructMemberDescriptionFactory { + ty: struct_type, + variant, + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Tuples +//=----------------------------------------------------------------------------- + +// Creates MemberDescriptions for the fields of a tuple +struct TupleMemberDescriptionFactory<'tcx> { + ty: Ty<'tcx>, + component_types: Vec>, + span: Span, +} + +impl<'tcx> TupleMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let layout = cx.layout_of(self.ty); + self.component_types.iter().enumerate().map(|(i, &component_type)| { + let (size, align) = cx.size_and_align_of(component_type); + MemberDescription { + name: format!("__{}", i), + type_metadata: type_metadata(cx, component_type, self.span), + offset: layout.fields.offset(i), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + +fn prepare_tuple_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + tuple_type: Ty<'tcx>, + component_types: &[Ty<'tcx>], + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); + + create_and_register_recursive_type_forward_declaration( + cx, + tuple_type, + unique_type_id, + create_struct_stub(cx, + tuple_type, + &tuple_name[..], + unique_type_id, + NO_SCOPE_METADATA), + TupleMDF(TupleMemberDescriptionFactory { + ty: tuple_type, + component_types: component_types.to_vec(), + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Unions +//=----------------------------------------------------------------------------- + +struct UnionMemberDescriptionFactory<'tcx> { + layout: TyLayout<'tcx>, + variant: &'tcx ty::VariantDef, + span: Span, +} + +impl<'tcx> UnionMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + self.variant.fields.iter().enumerate().map(|(i, f)| { + let field = self.layout.field(cx, i); + let (size, align) = field.size_and_align(); + MemberDescription { + name: f.name.to_string(), + type_metadata: type_metadata(cx, field.ty, self.span), + offset: Size::from_bytes(0), + size, + align, + flags: DIFlags::FlagZero, + } + }).collect() + } +} + +fn prepare_union_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + union_type: Ty<'tcx>, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let union_name = compute_debuginfo_type_name(cx, union_type, false); + + let (union_def_id, variant) = match union_type.sty { + ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), + _ => bug!("prepare_union_metadata on a non-ADT") + }; + + let containing_scope = get_namespace_for_item(cx, union_def_id); + + let union_metadata_stub = create_union_stub(cx, + union_type, + &union_name, + unique_type_id, + containing_scope); + + create_and_register_recursive_type_forward_declaration( + cx, + union_type, + unique_type_id, + union_metadata_stub, + UnionMDF(UnionMemberDescriptionFactory { + layout: cx.layout_of(union_type), + variant, + span, + }) + ) +} + +//=----------------------------------------------------------------------------- +// Enums +//=----------------------------------------------------------------------------- + +// Describes the members of an enum value: An enum is described as a union of +// structs in DWARF. This MemberDescriptionFactory provides the description for +// the members of this union; so for every variant of the given enum, this +// factory will produce one MemberDescription (all with no name and a fixed +// offset of zero bytes). +struct EnumMemberDescriptionFactory<'tcx> { + enum_type: Ty<'tcx>, + layout: TyLayout<'tcx>, + discriminant_type_metadata: Option, + containing_scope: DIScope, + span: Span, +} + +impl<'tcx> EnumMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + let adt = &self.enum_type.ty_adt_def().unwrap(); + match self.layout.variants { + layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], + layout::Variants::Single { index } => { + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + self.layout, + &adt.variants[index], + NoDiscriminant, + self.containing_scope, + self.span); + + let member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &member_descriptions[..]); + vec![ + MemberDescription { + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: self.layout.size, + align: self.layout.align, + flags: DIFlags::FlagZero + } + ] + } + layout::Variants::Tagged { ref variants, .. } => { + let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata + .expect("")); + (0..variants.len()).map(|i| { + let variant = self.layout.for_variant(cx, i); + let (variant_type_metadata, member_desc_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[i], + discriminant_info, + self.containing_scope, + self.span); + + let member_descriptions = member_desc_factory + .create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &member_descriptions); + MemberDescription { + name: "".to_string(), + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, + flags: DIFlags::FlagZero + } + }).collect() + } + layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { + let variant = self.layout.for_variant(cx, dataful_variant); + // Create a description of the non-null variant + let (variant_type_metadata, member_description_factory) = + describe_enum_variant(cx, + variant, + &adt.variants[dataful_variant], + OptimizedDiscriminant, + self.containing_scope, + self.span); + + let variant_member_descriptions = + member_description_factory.create_member_descriptions(cx); + + set_members_of_composite_type(cx, + variant_type_metadata, + &variant_member_descriptions[..]); + + // Encode the information about the null variant in the union + // member's name. + let mut name = String::from("RUST$ENCODED$ENUM$"); + // HACK(eddyb) the debuggers should just handle offset+size + // of discriminant instead of us having to recover its path. + // Right now it's not even going to work for `niche_start > 0`, + // and for multiple niche variants it only supports the first. + fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &mut String, + layout: TyLayout<'tcx>, + offset: Size, + size: Size) { + for i in 0..layout.fields.count() { + let field_offset = layout.fields.offset(i); + if field_offset > offset { + continue; + } + let inner_offset = offset - field_offset; + let field = layout.field(cx, i); + if inner_offset + size <= field.size { + write!(name, "{}$", i).unwrap(); + compute_field_path(cx, name, field, inner_offset, size); + } + } + } + compute_field_path(cx, &mut name, + self.layout, + self.layout.fields.offset(0), + self.layout.field(cx, 0).size); + name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); + + // Create the (singleton) list of descriptions of union members. + vec![ + MemberDescription { + name, + type_metadata: variant_type_metadata, + offset: Size::from_bytes(0), + size: variant.size, + align: variant.align, + flags: DIFlags::FlagZero + } + ] + } + } + } +} + +// Creates MemberDescriptions for the fields of a single enum variant. +struct VariantMemberDescriptionFactory<'tcx> { + // Cloned from the layout::Struct describing the variant. + offsets: Vec, + args: Vec<(String, Ty<'tcx>)>, + discriminant_type_metadata: Option, + span: Span, +} + +impl<'tcx> VariantMemberDescriptionFactory<'tcx> { + fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) + -> Vec { + self.args.iter().enumerate().map(|(i, &(ref name, ty))| { + let (size, align) = cx.size_and_align_of(ty); + MemberDescription { + name: name.to_string(), + type_metadata: match self.discriminant_type_metadata { + Some(metadata) if i == 0 => metadata, + _ => type_metadata(cx, ty, self.span) + }, + offset: self.offsets[i], + size, + align, + flags: DIFlags::FlagZero + } + }).collect() + } +} + +#[derive(Copy, Clone)] +enum EnumDiscriminantInfo { + RegularDiscriminant(DIType), + OptimizedDiscriminant, + NoDiscriminant +} + +// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type +// of the variant, and (3) a MemberDescriptionFactory for producing the +// descriptions of the fields of the variant. This is a rudimentary version of a +// full RecursiveTypeDescription. +fn describe_enum_variant<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: layout::TyLayout<'tcx>, + variant: &'tcx ty::VariantDef, + discriminant_info: EnumDiscriminantInfo, + containing_scope: DIScope, + span: Span) + -> (DICompositeType, MemberDescriptionFactory<'tcx>) { + let variant_name = variant.name.as_str(); + let unique_type_id = debug_context(cx).type_map + .borrow_mut() + .get_unique_type_id_of_enum_variant( + cx, + layout.ty, + &variant_name); + + let metadata_stub = create_struct_stub(cx, + layout.ty, + &variant_name, + unique_type_id, + containing_scope); + + // If this is not a univariant enum, there is also the discriminant field. + let (discr_offset, discr_arg) = match discriminant_info { + RegularDiscriminant(_) => { + let enum_layout = cx.layout_of(layout.ty); + (Some(enum_layout.fields.offset(0)), + Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty))) + } + _ => (None, None), + }; + let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { + layout.fields.offset(i) + })).collect(); + + // Build an array of (field name, field type) pairs to be captured in the factory closure. + let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { + let name = if variant.ctor_kind == CtorKind::Fn { + format!("__{}", i) + } else { + variant.fields[i].name.to_string() + }; + (name, layout.field(cx, i).ty) + })).collect(); + + let member_description_factory = + VariantMDF(VariantMemberDescriptionFactory { + offsets, + args, + discriminant_type_metadata: match discriminant_info { + RegularDiscriminant(discriminant_type_metadata) => { + Some(discriminant_type_metadata) + } + _ => None + }, + span, + }); + + (metadata_stub, member_description_factory) +} + +fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + enum_type: Ty<'tcx>, + enum_def_id: DefId, + unique_type_id: UniqueTypeId, + span: Span) + -> RecursiveTypeDescription<'tcx> { + let enum_name = compute_debuginfo_type_name(cx, enum_type, false); + + let containing_scope = get_namespace_for_item(cx, enum_def_id); + // FIXME: This should emit actual file metadata for the enum, but we + // currently can't get the necessary information when it comes to types + // imported from other crates. Formerly we violated the ODR when performing + // LTO because we emitted debuginfo for the same type with varying file + // metadata, so as a workaround we pretend that the type comes from + // + let file_metadata = unknown_file_metadata(cx); + + let def = enum_type.ty_adt_def().unwrap(); + let enumerators_metadata: Vec = def.discriminants(cx.tcx) + .zip(&def.variants) + .map(|(discr, v)| { + let token = v.name.as_str(); + let name = CString::new(token.as_bytes()).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreateEnumerator( + DIB(cx), + name.as_ptr(), + // FIXME: what if enumeration has i128 discriminant? + discr.val as u64) + } + }) + .collect(); + + let discriminant_type_metadata = |discr: layout::Primitive| { + let disr_type_key = (enum_def_id, discr); + let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types + .borrow() + .get(&disr_type_key).cloned(); + match cached_discriminant_type_metadata { + Some(discriminant_type_metadata) => discriminant_type_metadata, + None => { + let (discriminant_size, discriminant_align) = + (discr.size(cx), discr.align(cx)); + let discriminant_base_type_metadata = + type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); + let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str(); + + let name = CString::new(discriminant_name.as_bytes()).unwrap(); + let discriminant_type_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateEnumerationType( + DIB(cx), + containing_scope, + name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + discriminant_size.bits(), + discriminant_align.abi_bits() as u32, + create_DIArray(DIB(cx), &enumerators_metadata), + discriminant_base_type_metadata) + }; + + debug_context(cx).created_enum_disr_types + .borrow_mut() + .insert(disr_type_key, discriminant_type_metadata); + + discriminant_type_metadata + } + } + }; + + let layout = cx.layout_of(enum_type); + + let discriminant_type_metadata = match layout.variants { + layout::Variants::Single { .. } | + layout::Variants::NicheFilling { .. } => None, + layout::Variants::Tagged { ref tag, .. } => { + Some(discriminant_type_metadata(tag.value)) + } + }; + + match (&layout.abi, discriminant_type_metadata) { + (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), + _ => {} + } + + let (enum_type_size, enum_type_align) = layout.size_and_align(); + + let enum_name = CString::new(enum_name).unwrap(); + let unique_type_id_str = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let enum_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + enum_name.as_ptr(), + file_metadata, + UNKNOWN_LINE_NUMBER, + enum_type_size.bits(), + enum_type_align.abi_bits() as u32, + DIFlags::FlagZero, + ptr::null_mut(), + 0, // RuntimeLang + unique_type_id_str.as_ptr()) + }; + + return create_and_register_recursive_type_forward_declaration( + cx, + enum_type, + unique_type_id, + enum_metadata, + EnumMDF(EnumMemberDescriptionFactory { + enum_type, + layout, + discriminant_type_metadata, + containing_scope, + span, + }), + ); + + fn get_enum_discriminant_name(cx: &CodegenCx, + def_id: DefId) + -> InternedString { + cx.tcx.item_name(def_id) + } +} + +/// Creates debug information for a composite type, that is, anything that +/// results in a LLVM struct. +/// +/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. +fn composite_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + composite_type: Ty<'tcx>, + composite_type_name: &str, + composite_type_unique_id: UniqueTypeId, + member_descriptions: &[MemberDescription], + containing_scope: DIScope, + + // Ignore source location information as long as it + // can't be reconstructed for non-local crates. + _file_metadata: DIFile, + _definition_span: Span) + -> DICompositeType { + // Create the (empty) struct metadata node ... + let composite_type_metadata = create_struct_stub(cx, + composite_type, + composite_type_name, + composite_type_unique_id, + containing_scope); + // ... and immediately create and add the member descriptions. + set_members_of_composite_type(cx, + composite_type_metadata, + member_descriptions); + + return composite_type_metadata; +} + +fn set_members_of_composite_type(cx: &CodegenCx, + composite_type_metadata: DICompositeType, + member_descriptions: &[MemberDescription]) { + // In some rare cases LLVM metadata uniquing would lead to an existing type + // description being used instead of a new one created in + // create_struct_stub. This would cause a hard to trace assertion in + // DICompositeType::SetTypeArray(). The following check makes sure that we + // get a better error message if this should happen again due to some + // regression. + { + let mut composite_types_completed = + debug_context(cx).composite_types_completed.borrow_mut(); + if composite_types_completed.contains(&composite_type_metadata) { + bug!("debuginfo::set_members_of_composite_type() - \ + Already completed forward declaration re-encountered."); + } else { + composite_types_completed.insert(composite_type_metadata); + } + } + + let member_metadata: Vec = member_descriptions + .iter() + .map(|member_description| { + let member_name = member_description.name.as_bytes(); + let member_name = CString::new(member_name).unwrap(); + unsafe { + llvm::LLVMRustDIBuilderCreateMemberType( + DIB(cx), + composite_type_metadata, + member_name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + member_description.size.bits(), + member_description.align.abi_bits() as u32, + member_description.offset.bits(), + member_description.flags, + member_description.type_metadata) + } + }) + .collect(); + + unsafe { + let type_array = create_DIArray(DIB(cx), &member_metadata[..]); + llvm::LLVMRustDICompositeTypeSetTypeArray( + DIB(cx), composite_type_metadata, type_array); + } +} + +// A convenience wrapper around LLVMRustDIBuilderCreateStructType(). Does not do +// any caching, does not add any fields to the struct. This can be done later +// with set_members_of_composite_type(). +fn create_struct_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + struct_type: Ty<'tcx>, + struct_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (struct_size, struct_align) = cx.size_and_align_of(struct_type); + + let name = CString::new(struct_type_name).unwrap(); + let unique_type_id = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let metadata_stub = unsafe { + // LLVMRustDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + containing_scope, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + struct_size.bits(), + struct_align.abi_bits() as u32, + DIFlags::FlagZero, + ptr::null_mut(), + empty_array, + 0, + ptr::null_mut(), + unique_type_id.as_ptr()) + }; + + return metadata_stub; +} + +fn create_union_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + union_type: Ty<'tcx>, + union_type_name: &str, + unique_type_id: UniqueTypeId, + containing_scope: DIScope) + -> DICompositeType { + let (union_size, union_align) = cx.size_and_align_of(union_type); + + let name = CString::new(union_type_name).unwrap(); + let unique_type_id = CString::new( + debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() + ).unwrap(); + let metadata_stub = unsafe { + // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + llvm::LLVMRustDIBuilderCreateUnionType( + DIB(cx), + containing_scope, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + union_size.bits(), + union_align.abi_bits() as u32, + DIFlags::FlagZero, + empty_array, + 0, // RuntimeLang + unique_type_id.as_ptr()) + }; + + return metadata_stub; +} + +/// Creates debug information for the given global variable. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_global_var_metadata(cx: &CodegenCx, + def_id: DefId, + global: ValueRef) { + if cx.dbg_cx.is_none() { + return; + } + + let tcx = cx.tcx; + let attrs = tcx.codegen_fn_attrs(def_id); + + if attrs.flags.contains(CodegenFnAttrFlags::NO_DEBUG) { + return; + } + + let no_mangle = attrs.flags.contains(CodegenFnAttrFlags::NO_MANGLE); + // We may want to remove the namespace scope if we're in an extern block, see: + // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952 + let var_scope = get_namespace_for_item(cx, def_id); + let span = tcx.def_span(def_id); + + let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP { + let loc = span_start(cx, span); + (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint) + } else { + (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) + }; + + let is_local_to_unit = is_node_local_to_unit(cx, def_id); + let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx); + let type_metadata = type_metadata(cx, variable_type, span); + let var_name = tcx.item_name(def_id).to_string(); + let var_name = CString::new(var_name).unwrap(); + let linkage_name = if no_mangle { + None + } else { + let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)); + Some(CString::new(linkage_name.to_string()).unwrap()) + }; + + let global_align = cx.align_of(variable_type); + + unsafe { + llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), + var_scope, + var_name.as_ptr(), + // If null, linkage_name field is omitted, + // which is what we want for no_mangle statics + linkage_name.as_ref() + .map_or(ptr::null(), |name| name.as_ptr()), + file_metadata, + line_number, + type_metadata, + is_local_to_unit, + global, + ptr::null_mut(), + global_align.abi() as u32, + ); + } +} + +// Creates an "extension" of an existing DIScope into another file. +pub fn extend_scope_to_file(cx: &CodegenCx, + scope_metadata: DIScope, + file: &syntax_pos::FileMap, + defining_crate: CrateNum) + -> DILexicalBlock { + let file_metadata = file_metadata(cx, &file.name, defining_crate); + unsafe { + llvm::LLVMRustDIBuilderCreateLexicalBlockFile( + DIB(cx), + scope_metadata, + file_metadata) + } +} + +/// Creates debug information for the given vtable, which is for the +/// given type. +/// +/// Adds the created metadata nodes directly to the crate's IR. +pub fn create_vtable_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: ty::Ty<'tcx>, + vtable: ValueRef) { + if cx.dbg_cx.is_none() { + return; + } + + let type_metadata = type_metadata(cx, ty, syntax_pos::DUMMY_SP); + + unsafe { + // LLVMRustDIBuilderCreateStructType() wants an empty array. A null + // pointer will lead to hard to trace and debug LLVM assertions + // later on in llvm/lib/IR/Value.cpp. + let empty_array = create_DIArray(DIB(cx), &[]); + + let name = CString::new("vtable").unwrap(); + + // Create a new one each time. We don't want metadata caching + // here, because each vtable will refer to a unique containing + // type. + let vtable_type = llvm::LLVMRustDIBuilderCreateStructType( + DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + Size::from_bytes(0).bits(), + cx.tcx.data_layout.pointer_align.abi_bits() as u32, + DIFlags::FlagArtificial, + ptr::null_mut(), + empty_array, + 0, + type_metadata, + name.as_ptr() + ); + + llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), + NO_SCOPE_METADATA, + name.as_ptr(), + // LLVM 3.9 + // doesn't accept + // null here, so + // pass the name + // as the linkage + // name. + name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER, + vtable_type, + true, + vtable, + ptr::null_mut(), + 0); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/mod.rs b/src/librustc_codegen_llvm/debuginfo/mod.rs new file mode 100644 index 00000000000..294d8cbbd93 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/mod.rs @@ -0,0 +1,542 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// See doc.rs for documentation. +mod doc; + +use self::VariableAccess::*; +use self::VariableKind::*; + +use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit}; +use self::namespace::mangled_name_of_instance; +use self::type_names::compute_debuginfo_type_name; +use self::metadata::{type_metadata, file_metadata, TypeMap}; +use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; + +use llvm; +use llvm::{ModuleRef, ContextRef, ValueRef}; +use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags}; +use rustc::hir::CodegenFnAttrFlags; +use rustc::hir::def_id::{DefId, CrateNum}; +use rustc::ty::subst::{Substs, UnpackedKind}; + +use abi::Abi; +use common::CodegenCx; +use builder::Builder; +use monomorphize::Instance; +use rustc::ty::{self, ParamEnv, Ty, InstanceDef}; +use rustc::mir; +use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; +use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; + +use libc::c_uint; +use std::cell::{Cell, RefCell}; +use std::ffi::CString; +use std::ptr; + +use syntax_pos::{self, Span, Pos}; +use syntax::ast; +use syntax::symbol::{Symbol, InternedString}; +use rustc::ty::layout::{self, LayoutOf}; + +pub mod gdb; +mod utils; +mod namespace; +mod type_names; +pub mod metadata; +mod create_scope_map; +mod source_loc; + +pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; +pub use self::source_loc::start_emitting_source_locations; +pub use self::metadata::create_global_var_metadata; +pub use self::metadata::create_vtable_metadata; +pub use self::metadata::extend_scope_to_file; +pub use self::source_loc::set_source_location; + +#[allow(non_upper_case_globals)] +const DW_TAG_auto_variable: c_uint = 0x100; +#[allow(non_upper_case_globals)] +const DW_TAG_arg_variable: c_uint = 0x101; + +/// A context object for maintaining all state needed by the debuginfo module. +pub struct CrateDebugContext<'tcx> { + llcontext: ContextRef, + llmod: ModuleRef, + builder: DIBuilderRef, + created_files: RefCell>, + created_enum_disr_types: RefCell>, + + type_map: RefCell>, + namespace_map: RefCell>, + + // This collection is used to assert that composite types (structs, enums, + // ...) have their members only set once: + composite_types_completed: RefCell>, +} + +impl<'tcx> CrateDebugContext<'tcx> { + pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { + debug!("CrateDebugContext::new"); + let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; + // DIBuilder inherits context from the module, so we'd better use the same one + let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; + CrateDebugContext { + llcontext, + llmod, + builder, + created_files: RefCell::new(FxHashMap()), + created_enum_disr_types: RefCell::new(FxHashMap()), + type_map: RefCell::new(TypeMap::new()), + namespace_map: RefCell::new(DefIdMap()), + composite_types_completed: RefCell::new(FxHashSet()), + } + } +} + +pub enum FunctionDebugContext { + RegularContext(FunctionDebugContextData), + DebugInfoDisabled, + FunctionWithoutDebugInfo, +} + +impl FunctionDebugContext { + pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { + match *self { + FunctionDebugContext::RegularContext(ref data) => data, + FunctionDebugContext::DebugInfoDisabled => { + span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message()); + } + FunctionDebugContext::FunctionWithoutDebugInfo => { + span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message()); + } + } + } + + fn debuginfo_disabled_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" + } + + fn should_be_ignored_message() -> &'static str { + "debuginfo: Error trying to access FunctionDebugContext for function that should be \ + ignored by debug info!" + } +} + +pub struct FunctionDebugContextData { + fn_metadata: DISubprogram, + source_locations_enabled: Cell, + pub defining_crate: CrateNum, +} + +pub enum VariableAccess<'a> { + // The llptr given is an alloca containing the variable's value + DirectVariable { alloca: ValueRef }, + // The llptr given is an alloca containing the start of some pointer chain + // leading to the variable's content. + IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } +} + +pub enum VariableKind { + ArgumentVariable(usize /*index*/), + LocalVariable, + CapturedVariable, +} + +/// Create any deferred debug metadata nodes +pub fn finalize(cx: &CodegenCx) { + if cx.dbg_cx.is_none() { + return; + } + + debug!("finalize"); + + if gdb::needs_gdb_debug_scripts_section(cx) { + // Add a .debug_gdb_scripts section to this compile-unit. This will + // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, + // which activates the Rust pretty printers for binary this section is + // contained in. + gdb::get_or_insert_gdb_debug_scripts_section_global(cx); + } + + unsafe { + llvm::LLVMRustDIBuilderFinalize(DIB(cx)); + llvm::LLVMRustDIBuilderDispose(DIB(cx)); + // Debuginfo generation in LLVM by default uses a higher + // version of dwarf than macOS currently understands. We can + // instruct LLVM to emit an older version of dwarf, however, + // for macOS to understand. For more info see #11352 + // This can be overridden using --llvm-opts -dwarf-version,N. + // Android has the same issue (#22398) + if cx.sess().target.target.options.is_like_osx || + cx.sess().target.target.options.is_like_android { + llvm::LLVMRustAddModuleFlag(cx.llmod, + "Dwarf Version\0".as_ptr() as *const _, + 2) + } + + // Indicate that we want CodeView debug information on MSVC + if cx.sess().target.target.options.is_like_msvc { + llvm::LLVMRustAddModuleFlag(cx.llmod, + "CodeView\0".as_ptr() as *const _, + 1) + } + + // Prevent bitcode readers from deleting the debug info. + let ptr = "Debug Info Version\0".as_ptr(); + llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _, + llvm::LLVMRustDebugMetadataVersion()); + }; +} + +/// Creates the function-specific debug context. +/// +/// Returns the FunctionDebugContext for the function which holds state needed +/// for debug info creation. The function may also return another variant of the +/// FunctionDebugContext enum which indicates why no debuginfo should be created +/// for the function. +pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, + sig: ty::FnSig<'tcx>, + llfn: ValueRef, + mir: &mir::Mir) -> FunctionDebugContext { + if cx.sess().opts.debuginfo == NoDebugInfo { + return FunctionDebugContext::DebugInfoDisabled; + } + + if let InstanceDef::Item(def_id) = instance.def { + if cx.tcx.codegen_fn_attrs(def_id).flags.contains(CodegenFnAttrFlags::NO_DEBUG) { + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + } + + let span = mir.span; + + // This can be the case for functions inlined from another crate + if span == syntax_pos::DUMMY_SP { + // FIXME(simulacrum): Probably can't happen; remove. + return FunctionDebugContext::FunctionWithoutDebugInfo; + } + + let def_id = instance.def_id(); + let containing_scope = get_containing_scope(cx, instance); + let loc = span_start(cx, span); + let file_metadata = file_metadata(cx, &loc.file.name, def_id.krate); + + let function_type_metadata = unsafe { + let fn_signature = get_function_signature(cx, sig); + llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) + }; + + // Find the enclosing function, in case this is a closure. + let def_key = cx.tcx.def_key(def_id); + let mut name = def_key.disambiguated_data.data.to_string(); + + let enclosing_fn_def_id = cx.tcx.closure_base_def_id(def_id); + + // Get_template_parameters() will append a `<...>` clause to the function + // name if necessary. + let generics = cx.tcx.generics_of(enclosing_fn_def_id); + let substs = instance.substs.truncate_to(cx.tcx, generics); + let template_parameters = get_template_parameters(cx, + &generics, + substs, + file_metadata, + &mut name); + + // Get the linkage_name, which is just the symbol name + let linkage_name = mangled_name_of_instance(cx, instance); + + let scope_line = span_start(cx, span).line; + let is_local_to_unit = is_node_local_to_unit(cx, def_id); + + let function_name = CString::new(name).unwrap(); + let linkage_name = CString::new(linkage_name.to_string()).unwrap(); + + let mut flags = DIFlags::FlagPrototyped; + + let local_id = cx.tcx.hir.as_local_node_id(def_id); + match *cx.sess().entry_fn.borrow() { + Some((id, _, _)) => { + if local_id == Some(id) { + flags = flags | DIFlags::FlagMainSubprogram; + } + } + None => {} + }; + if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited { + flags = flags | DIFlags::FlagNoReturn; + } + + let fn_metadata = unsafe { + llvm::LLVMRustDIBuilderCreateFunction( + DIB(cx), + containing_scope, + function_name.as_ptr(), + linkage_name.as_ptr(), + file_metadata, + loc.line as c_uint, + function_type_metadata, + is_local_to_unit, + true, + scope_line as c_uint, + flags, + cx.sess().opts.optimize != config::OptLevel::No, + llfn, + template_parameters, + ptr::null_mut()) + }; + + // Initialize fn debug context (including scope map and namespace map) + let fn_debug_context = FunctionDebugContextData { + fn_metadata, + source_locations_enabled: Cell::new(false), + defining_crate: def_id.krate, + }; + + return FunctionDebugContext::RegularContext(fn_debug_context); + + fn get_function_signature<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + sig: ty::FnSig<'tcx>) -> DIArray { + if cx.sess().opts.debuginfo == LimitedDebugInfo { + return create_DIArray(DIB(cx), &[]); + } + + let mut signature = Vec::with_capacity(sig.inputs().len() + 1); + + // Return type -- llvm::DIBuilder wants this at index 0 + signature.push(match sig.output().sty { + ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), + _ => type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP) + }); + + let inputs = if sig.abi == Abi::RustCall { + &sig.inputs()[..sig.inputs().len() - 1] + } else { + sig.inputs() + }; + + // Arguments types + if cx.sess().target.target.options.is_like_msvc { + // FIXME(#42800): + // There is a bug in MSDIA that leads to a crash when it encounters + // a fixed-size array of `u8` or something zero-sized in a + // function-type (see #40477). + // As a workaround, we replace those fixed-size arrays with a + // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would + // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, + // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. + // This transformed type is wrong, but these function types are + // already inaccurate due to ABI adjustments (see #42800). + signature.extend(inputs.iter().map(|&t| { + let t = match t.sty { + ty::TyArray(ct, _) + if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { + cx.tcx.mk_imm_ptr(ct) + } + _ => t + }; + type_metadata(cx, t, syntax_pos::DUMMY_SP) + })); + } else { + signature.extend(inputs.iter().map(|t| { + type_metadata(cx, t, syntax_pos::DUMMY_SP) + })); + } + + if sig.abi == Abi::RustCall && !sig.inputs().is_empty() { + if let ty::TyTuple(args) = sig.inputs()[sig.inputs().len() - 1].sty { + for &argument_type in args { + signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP)); + } + } + } + + return create_DIArray(DIB(cx), &signature[..]); + } + + fn get_template_parameters<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + generics: &ty::Generics, + substs: &Substs<'tcx>, + file_metadata: DIFile, + name_to_append_suffix_to: &mut String) + -> DIArray + { + if substs.types().next().is_none() { + return create_DIArray(DIB(cx), &[]); + } + + name_to_append_suffix_to.push('<'); + for (i, actual_type) in substs.types().enumerate() { + if i != 0 { + name_to_append_suffix_to.push_str(","); + } + + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); + // Add actual type name to <...> clause of function name + let actual_type_name = compute_debuginfo_type_name(cx, + actual_type, + true); + name_to_append_suffix_to.push_str(&actual_type_name[..]); + } + name_to_append_suffix_to.push('>'); + + // Again, only create type information if full debuginfo is enabled + let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { + let names = get_parameter_names(cx, generics); + substs.iter().zip(names).filter_map(|(kind, name)| { + if let UnpackedKind::Type(ty) = kind.unpack() { + let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); + let actual_type_metadata = + type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); + let name = CString::new(name.as_str().as_bytes()).unwrap(); + Some(unsafe { + llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( + DIB(cx), + ptr::null_mut(), + name.as_ptr(), + actual_type_metadata, + file_metadata, + 0, + 0) + }) + } else { + None + } + }).collect() + } else { + vec![] + }; + + return create_DIArray(DIB(cx), &template_params[..]); + } + + fn get_parameter_names(cx: &CodegenCx, + generics: &ty::Generics) + -> Vec { + let mut names = generics.parent.map_or(vec![], |def_id| { + get_parameter_names(cx, cx.tcx.generics_of(def_id)) + }); + names.extend(generics.params.iter().map(|param| param.name)); + names + } + + fn get_containing_scope<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, + instance: Instance<'tcx>) + -> DIScope { + // First, let's see if this is a method within an inherent impl. Because + // if yes, we want to make the result subroutine DIE a child of the + // subroutine's self-type. + let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { + // If the method does *not* belong to a trait, proceed + if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { + let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( + instance.substs, + ty::ParamEnv::reveal_all(), + &cx.tcx.type_of(impl_def_id), + ); + + // Only "class" methods are generally understood by LLVM, + // so avoid methods on other types (e.g. `<*mut T>::null`). + match impl_self_ty.sty { + ty::TyAdt(def, ..) if !def.is_box() => { + Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP)) + } + _ => None + } + } else { + // For trait method impls we still use the "parallel namespace" + // strategy + None + } + }); + + self_type.unwrap_or_else(|| { + namespace::item_namespace(cx, DefId { + krate: instance.def_id().krate, + index: cx.tcx + .def_key(instance.def_id()) + .parent + .expect("get_containing_scope: missing parent?") + }) + }) + } +} + +pub fn declare_local<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + dbg_context: &FunctionDebugContext, + variable_name: ast::Name, + variable_type: Ty<'tcx>, + scope_metadata: DIScope, + variable_access: VariableAccess, + variable_kind: VariableKind, + span: Span) { + let cx = bx.cx; + + let file = span_start(cx, span).file; + let file_metadata = file_metadata(cx, + &file.name, + dbg_context.get_ref(span).defining_crate); + + let loc = span_start(cx, span); + let type_metadata = type_metadata(cx, variable_type, span); + + let (argument_index, dwarf_tag) = match variable_kind { + ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), + LocalVariable | + CapturedVariable => (0, DW_TAG_auto_variable) + }; + let align = cx.align_of(variable_type); + + let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); + match (variable_access, &[][..]) { + (DirectVariable { alloca }, address_operations) | + (IndirectVariable {alloca, address_operations}, _) => { + let metadata = unsafe { + llvm::LLVMRustDIBuilderCreateVariable( + DIB(cx), + dwarf_tag, + scope_metadata, + name.as_ptr(), + file_metadata, + loc.line as c_uint, + type_metadata, + cx.sess().opts.optimize != config::OptLevel::No, + DIFlags::FlagZero, + argument_index, + align.abi() as u32, + ) + }; + source_loc::set_debug_location(bx, + InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); + unsafe { + let debug_loc = llvm::LLVMGetCurrentDebugLocation(bx.llbuilder); + let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( + DIB(cx), + alloca, + metadata, + address_operations.as_ptr(), + address_operations.len() as c_uint, + debug_loc, + bx.llbb()); + + llvm::LLVMSetInstDebugLocation(bx.llbuilder, instr); + } + } + } + + match variable_kind { + ArgumentVariable(_) | CapturedVariable => { + assert!(!dbg_context.get_ref(span).source_locations_enabled.get()); + source_loc::set_debug_location(bx, UnknownLocation); + } + _ => { /* nothing to do */ } + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/namespace.rs b/src/librustc_codegen_llvm/debuginfo/namespace.rs new file mode 100644 index 00000000000..51c45de9dc2 --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/namespace.rs @@ -0,0 +1,66 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Namespace Handling. + +use super::metadata::{unknown_file_metadata, UNKNOWN_LINE_NUMBER}; +use super::utils::{DIB, debug_context}; +use monomorphize::Instance; +use rustc::ty; + +use llvm; +use llvm::debuginfo::DIScope; +use rustc::hir::def_id::DefId; +use rustc::hir::map::DefPathData; +use common::CodegenCx; + +use std::ffi::CString; +use std::ptr; + +pub fn mangled_name_of_instance<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, +) -> ty::SymbolName { + let tcx = cx.tcx; + tcx.symbol_name(instance) +} + +pub fn item_namespace(cx: &CodegenCx, def_id: DefId) -> DIScope { + if let Some(&scope) = debug_context(cx).namespace_map.borrow().get(&def_id) { + return scope; + } + + let def_key = cx.tcx.def_key(def_id); + let parent_scope = def_key.parent.map_or(ptr::null_mut(), |parent| { + item_namespace(cx, DefId { + krate: def_id.krate, + index: parent + }) + }); + + let namespace_name = match def_key.disambiguated_data.data { + DefPathData::CrateRoot => cx.tcx.crate_name(def_id.krate).as_str(), + data => data.as_interned_str().as_str() + }; + + let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); + + let scope = unsafe { + llvm::LLVMRustDIBuilderCreateNameSpace( + DIB(cx), + parent_scope, + namespace_name.as_ptr(), + unknown_file_metadata(cx), + UNKNOWN_LINE_NUMBER) + }; + + debug_context(cx).namespace_map.borrow_mut().insert(def_id, scope); + scope +} diff --git a/src/librustc_codegen_llvm/debuginfo/source_loc.rs b/src/librustc_codegen_llvm/debuginfo/source_loc.rs new file mode 100644 index 00000000000..eb37e7f931c --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/source_loc.rs @@ -0,0 +1,107 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use self::InternalDebugLocation::*; + +use super::utils::{debug_context, span_start}; +use super::metadata::UNKNOWN_COLUMN_NUMBER; +use super::FunctionDebugContext; + +use llvm; +use llvm::debuginfo::DIScope; +use builder::Builder; + +use libc::c_uint; +use std::ptr; +use syntax_pos::{Span, Pos}; + +/// Sets the current debug location at the beginning of the span. +/// +/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). +pub fn set_source_location( + debug_context: &FunctionDebugContext, bx: &Builder, scope: DIScope, span: Span +) { + let function_debug_context = match *debug_context { + FunctionDebugContext::DebugInfoDisabled => return, + FunctionDebugContext::FunctionWithoutDebugInfo => { + set_debug_location(bx, UnknownLocation); + return; + } + FunctionDebugContext::RegularContext(ref data) => data + }; + + let dbg_loc = if function_debug_context.source_locations_enabled.get() { + debug!("set_source_location: {}", bx.sess().codemap().span_to_string(span)); + let loc = span_start(bx.cx, span); + InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) + } else { + UnknownLocation + }; + set_debug_location(bx, dbg_loc); +} + +/// Enables emitting source locations for the given functions. +/// +/// Since we don't want source locations to be emitted for the function prelude, +/// they are disabled when beginning to codegen a new function. This functions +/// switches source location emitting on and must therefore be called before the +/// first real statement/expression of the function is codegened. +pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) { + match *dbg_context { + FunctionDebugContext::RegularContext(ref data) => { + data.source_locations_enabled.set(true) + }, + _ => { /* safe to ignore */ } + } +} + + +#[derive(Copy, Clone, PartialEq)] +pub enum InternalDebugLocation { + KnownLocation { scope: DIScope, line: usize, col: usize }, + UnknownLocation +} + +impl InternalDebugLocation { + pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { + KnownLocation { + scope, + line, + col, + } + } +} + +pub fn set_debug_location(bx: &Builder, debug_location: InternalDebugLocation) { + let metadata_node = match debug_location { + KnownLocation { scope, line, .. } => { + // Always set the column to zero like Clang and GCC + let col = UNKNOWN_COLUMN_NUMBER; + debug!("setting debug location to {} {}", line, col); + + unsafe { + llvm::LLVMRustDIBuilderCreateDebugLocation( + debug_context(bx.cx).llcontext, + line as c_uint, + col as c_uint, + scope, + ptr::null_mut()) + } + } + UnknownLocation => { + debug!("clearing debug location "); + ptr::null_mut() + } + }; + + unsafe { + llvm::LLVMSetCurrentDebugLocation(bx.llbuilder, metadata_node); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/type_names.rs b/src/librustc_codegen_llvm/debuginfo/type_names.rs new file mode 100644 index 00000000000..05a74db3a6c --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/type_names.rs @@ -0,0 +1,224 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Type Names for Debug Info. + +use common::CodegenCx; +use rustc::hir::def_id::DefId; +use rustc::ty::subst::Substs; +use rustc::ty::{self, Ty}; + +use rustc::hir; + +// Compute the name of the type as it should be stored in debuginfo. Does not do +// any caching, i.e. calling the function twice with the same type will also do +// the work twice. The `qualified` parameter only affects the first level of the +// type name, further levels (i.e. type parameters) are always fully qualified. +pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool) + -> String { + let mut result = String::with_capacity(64); + push_debuginfo_type_name(cx, t, qualified, &mut result); + result +} + +// Pushes the name of the type as it should be stored in debuginfo on the +// `output` String. See also compute_debuginfo_type_name(). +pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + t: Ty<'tcx>, + qualified: bool, + output: &mut String) { + // When targeting MSVC, emit C++ style type names for compatibility with + // .natvis visualizers (and perhaps other existing native debuggers?) + let cpp_like_names = cx.sess().target.target.options.is_like_msvc; + + match t.sty { + ty::TyBool => output.push_str("bool"), + ty::TyChar => output.push_str("char"), + ty::TyStr => output.push_str("str"), + ty::TyNever => output.push_str("!"), + ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()), + ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), + ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), + ty::TyForeign(def_id) => push_item_name(cx, def_id, qualified, output), + ty::TyAdt(def, substs) => { + push_item_name(cx, def.did, qualified, output); + push_type_params(cx, substs, output); + }, + ty::TyTuple(component_types) => { + output.push('('); + for &component_type in component_types { + push_debuginfo_type_name(cx, component_type, true, output); + output.push_str(", "); + } + if !component_types.is_empty() { + output.pop(); + output.pop(); + } + output.push(')'); + }, + ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { + if !cpp_like_names { + output.push('*'); + } + match mutbl { + hir::MutImmutable => output.push_str("const "), + hir::MutMutable => output.push_str("mut "), + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('*'); + } + }, + ty::TyRef(_, inner_type, mutbl) => { + if !cpp_like_names { + output.push('&'); + } + if mutbl == hir::MutMutable { + output.push_str("mut "); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('*'); + } + }, + ty::TyArray(inner_type, len) => { + output.push('['); + push_debuginfo_type_name(cx, inner_type, true, output); + output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx))); + output.push(']'); + }, + ty::TySlice(inner_type) => { + if cpp_like_names { + output.push_str("slice<"); + } else { + output.push('['); + } + + push_debuginfo_type_name(cx, inner_type, true, output); + + if cpp_like_names { + output.push('>'); + } else { + output.push(']'); + } + }, + ty::TyDynamic(ref trait_data, ..) => { + if let Some(principal) = trait_data.principal() { + let principal = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &principal, + ); + push_item_name(cx, principal.def_id, false, output); + push_type_params(cx, principal.substs, output); + } + }, + ty::TyFnDef(..) | ty::TyFnPtr(_) => { + let sig = t.fn_sig(cx.tcx); + if sig.unsafety() == hir::Unsafety::Unsafe { + output.push_str("unsafe "); + } + + let abi = sig.abi(); + if abi != ::abi::Abi::Rust { + output.push_str("extern \""); + output.push_str(abi.name()); + output.push_str("\" "); + } + + output.push_str("fn("); + + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + if !sig.inputs().is_empty() { + for ¶meter_type in sig.inputs() { + push_debuginfo_type_name(cx, parameter_type, true, output); + output.push_str(", "); + } + output.pop(); + output.pop(); + } + + if sig.variadic { + if !sig.inputs().is_empty() { + output.push_str(", ..."); + } else { + output.push_str("..."); + } + } + + output.push(')'); + + if !sig.output().is_nil() { + output.push_str(" -> "); + push_debuginfo_type_name(cx, sig.output(), true, output); + } + }, + ty::TyClosure(..) => { + output.push_str("closure"); + } + ty::TyGenerator(..) => { + output.push_str("generator"); + } + ty::TyError | + ty::TyInfer(_) | + ty::TyProjection(..) | + ty::TyAnon(..) | + ty::TyGeneratorWitness(..) | + ty::TyParam(_) => { + bug!("debuginfo: Trying to create type name for \ + unexpected type: {:?}", t); + } + } + + fn push_item_name(cx: &CodegenCx, + def_id: DefId, + qualified: bool, + output: &mut String) { + if qualified { + output.push_str(&cx.tcx.crate_name(def_id.krate).as_str()); + for path_element in cx.tcx.def_path(def_id).data { + output.push_str("::"); + output.push_str(&path_element.data.as_interned_str().as_str()); + } + } else { + output.push_str(&cx.tcx.item_name(def_id).as_str()); + } + } + + // Pushes the type parameters in the given `Substs` to the output string. + // This ignores region parameters, since they can't reliably be + // reconstructed for items from non-local crates. For local crates, this + // would be possible but with inlining and LTO we have to use the least + // common denominator - otherwise we would run into conflicts. + fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + substs: &Substs<'tcx>, + output: &mut String) { + if substs.types().next().is_none() { + return; + } + + output.push('<'); + + for type_parameter in substs.types() { + push_debuginfo_type_name(cx, type_parameter, true, output); + output.push_str(", "); + } + + output.pop(); + output.pop(); + + output.push('>'); + } +} diff --git a/src/librustc_codegen_llvm/debuginfo/utils.rs b/src/librustc_codegen_llvm/debuginfo/utils.rs new file mode 100644 index 00000000000..9d37f99cb2a --- /dev/null +++ b/src/librustc_codegen_llvm/debuginfo/utils.rs @@ -0,0 +1,65 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Utility Functions. + +use super::{CrateDebugContext}; +use super::namespace::item_namespace; + +use rustc::hir::def_id::DefId; +use rustc::ty::DefIdTree; + +use llvm; +use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; +use common::{CodegenCx}; + +use syntax_pos::{self, Span}; + +pub fn is_node_local_to_unit(cx: &CodegenCx, def_id: DefId) -> bool +{ + // The is_local_to_unit flag indicates whether a function is local to the + // current compilation unit (i.e. if it is *static* in the C-sense). The + // *reachable* set should provide a good approximation of this, as it + // contains everything that might leak out of the current crate (by being + // externally visible or by being inlined into something externally + // visible). It might better to use the `exported_items` set from + // `driver::CrateAnalysis` in the future, but (atm) this set is not + // available in the codegen pass. + !cx.tcx.is_reachable_non_generic(def_id) +} + +#[allow(non_snake_case)] +pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { + return unsafe { + llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) + }; +} + +/// Return syntax_pos::Loc corresponding to the beginning of the span +pub fn span_start(cx: &CodegenCx, span: Span) -> syntax_pos::Loc { + cx.sess().codemap().lookup_char_pos(span.lo()) +} + +#[inline] +pub fn debug_context<'a, 'tcx>(cx: &'a CodegenCx<'a, 'tcx>) + -> &'a CrateDebugContext<'tcx> { + cx.dbg_cx.as_ref().unwrap() +} + +#[inline] +#[allow(non_snake_case)] +pub fn DIB(cx: &CodegenCx) -> DIBuilderRef { + cx.dbg_cx.as_ref().unwrap().builder +} + +pub fn get_namespace_for_item(cx: &CodegenCx, def_id: DefId) -> DIScope { + item_namespace(cx, cx.tcx.parent(def_id) + .expect("get_namespace_for_item: missing parent?")) +} diff --git a/src/librustc_codegen_llvm/declare.rs b/src/librustc_codegen_llvm/declare.rs new file mode 100644 index 00000000000..fdc84f914f5 --- /dev/null +++ b/src/librustc_codegen_llvm/declare.rs @@ -0,0 +1,223 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. +//! Declare various LLVM values. +//! +//! Prefer using functions and methods from this module rather than calling LLVM +//! functions directly. These functions do some additional work to ensure we do +//! the right thing given the preconceptions of codegen. +//! +//! Some useful guidelines: +//! +//! * Use declare_* family of methods if you are declaring, but are not +//! interested in defining the ValueRef they return. +//! * Use define_* family of methods when you might be defining the ValueRef. +//! * When in doubt, define. + +use llvm::{self, ValueRef}; +use llvm::AttributePlace::Function; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::session::config::Sanitizer; +use rustc_target::spec::PanicStrategy; +use abi::{Abi, FnType, FnTypeExt}; +use attributes; +use context::CodegenCx; +use common; +use type_::Type; +use value::Value; + +use std::ffi::CString; + + +/// Declare a global value. +/// +/// If there’s a value with the same name already declared, the function will +/// return its ValueRef instead. +pub fn declare_global(cx: &CodegenCx, name: &str, ty: Type) -> llvm::ValueRef { + debug!("declare_global(name={:?})", name); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + unsafe { + llvm::LLVMRustGetOrInsertGlobal(cx.llmod, namebuf.as_ptr(), ty.to_ref()) + } +} + + +/// Declare a function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +fn declare_raw_fn(cx: &CodegenCx, name: &str, callconv: llvm::CallConv, ty: Type) -> ValueRef { + debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + let llfn = unsafe { + llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty.to_ref()) + }; + + llvm::SetFunctionCallConv(llfn, callconv); + // Function addresses in Rust are never significant, allowing functions to + // be merged. + llvm::SetUnnamedAddr(llfn, true); + + if cx.tcx.sess.opts.cg.no_redzone + .unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { + llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); + } + + if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { + match *sanitizer { + Sanitizer::Address => { + llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); + }, + Sanitizer::Memory => { + llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); + }, + Sanitizer::Thread => { + llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); + }, + _ => {} + } + } + + match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) { + Some("s") => { + llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); + }, + Some("z") => { + llvm::Attribute::MinSize.apply_llfn(Function, llfn); + llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); + }, + _ => {}, + } + + if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind { + attributes::unwind(llfn, false); + } + + llfn +} + + +/// Declare a C ABI function. +/// +/// Only use this for foreign function ABIs and glue. For Rust functions use +/// `declare_fn` instead. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_cfn(cx: &CodegenCx, name: &str, fn_type: Type) -> ValueRef { + declare_raw_fn(cx, name, llvm::CCallConv, fn_type) +} + + +/// Declare a Rust function. +/// +/// If there’s a value with the same name already declared, the function will +/// update the declaration and return existing ValueRef instead. +pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); + let sig = common::ty_fn_sig(cx, fn_type); + let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + debug!("declare_rust_fn (after region erasure) sig={:?}", sig); + + let fty = FnType::new(cx, sig, &[]); + let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx)); + + if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited { + llvm::Attribute::NoReturn.apply_llfn(Function, llfn); + } + + if sig.abi != Abi::Rust && sig.abi != Abi::RustCall { + attributes::unwind(llfn, false); + } + + fty.apply_attrs_llfn(llfn); + + llfn +} + + +/// Declare a global with an intention to define it. +/// +/// Use this function when you intend to define a global. This function will +/// return None if the name already has a definition associated with it. In that +/// case an error should be reported to the user, because it usually happens due +/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). +pub fn define_global(cx: &CodegenCx, name: &str, ty: Type) -> Option { + if get_defined_value(cx, name).is_some() { + None + } else { + Some(declare_global(cx, name, ty)) + } +} + +/// Declare a Rust function with an intention to define it. +/// +/// Use this function when you intend to define a function. This function will +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + if get_defined_value(cx, name).is_some() { + cx.sess().fatal(&format!("symbol `{}` already defined", name)) + } else { + declare_fn(cx, name, fn_type) + } +} + +/// Declare a Rust function with an intention to define it. +/// +/// Use this function when you intend to define a function. This function will +/// return panic if the name already has a definition associated with it. This +/// can happen with #[no_mangle] or #[export_name], for example. +pub fn define_internal_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + fn_type: Ty<'tcx>) -> ValueRef { + let llfn = define_fn(cx, name, fn_type); + unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) }; + llfn +} + + +/// Get declared value by name. +pub fn get_declared_value(cx: &CodegenCx, name: &str) -> Option { + debug!("get_declared_value(name={:?})", name); + let namebuf = CString::new(name).unwrap_or_else(|_|{ + bug!("name {:?} contains an interior null byte", name) + }); + let val = unsafe { llvm::LLVMRustGetNamedValue(cx.llmod, namebuf.as_ptr()) }; + if val.is_null() { + debug!("get_declared_value: {:?} value is null", name); + None + } else { + debug!("get_declared_value: {:?} => {:?}", name, Value(val)); + Some(val) + } +} + +/// Get defined or externally defined (AvailableExternally linkage) value by +/// name. +pub fn get_defined_value(cx: &CodegenCx, name: &str) -> Option { + get_declared_value(cx, name).and_then(|val|{ + let declaration = unsafe { + llvm::LLVMIsDeclaration(val) != 0 + }; + if !declaration { + Some(val) + } else { + None + } + }) +} diff --git a/src/librustc_codegen_llvm/diagnostics.rs b/src/librustc_codegen_llvm/diagnostics.rs new file mode 100644 index 00000000000..57cc33d09bb --- /dev/null +++ b/src/librustc_codegen_llvm/diagnostics.rs @@ -0,0 +1,55 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_snake_case)] + +register_long_diagnostics! { + +E0511: r##" +Invalid monomorphization of an intrinsic function was used. Erroneous code +example: + +```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) +#![feature(platform_intrinsics)] + +extern "platform-intrinsic" { + fn simd_add(a: T, b: T) -> T; +} + +fn main() { + unsafe { simd_add(0, 1); } + // error: invalid monomorphization of `simd_add` intrinsic +} +``` + +The generic type has to be a SIMD type. Example: + +``` +#![feature(repr_simd)] +#![feature(platform_intrinsics)] + +#[repr(simd)] +#[derive(Copy, Clone)] +struct i32x2(i32, i32); + +extern "platform-intrinsic" { + fn simd_add(a: T, b: T) -> T; +} + +unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! +``` +"##, + +} + + +register_diagnostics! { + E0558 +} diff --git a/src/librustc_codegen_llvm/glue.rs b/src/librustc_codegen_llvm/glue.rs new file mode 100644 index 00000000000..c7275d09401 --- /dev/null +++ b/src/librustc_codegen_llvm/glue.rs @@ -0,0 +1,122 @@ +// Copyright 2012-2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! +// +// Code relating to drop glue. + +use std; + +use builder::Builder; +use common::*; +use llvm::{ValueRef}; +use llvm; +use meth; +use rustc::ty::layout::LayoutOf; +use rustc::ty::{self, Ty}; +use value::Value; + +pub fn size_and_align_of_dst<'a, 'tcx>(bx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) + -> (ValueRef, ValueRef) { + debug!("calculate size of DST: {}; with lost info: {:?}", + t, Value(info)); + if bx.cx.type_is_sized(t) { + let (size, align) = bx.cx.size_and_align_of(t); + debug!("size_and_align_of_dst t={} info={:?} size: {:?} align: {:?}", + t, Value(info), size, align); + let size = C_usize(bx.cx, size.bytes()); + let align = C_usize(bx.cx, align.abi()); + return (size, align); + } + assert!(!info.is_null()); + match t.sty { + ty::TyDynamic(..) => { + // load size/align from vtable + (meth::SIZE.get_usize(bx, info), meth::ALIGN.get_usize(bx, info)) + } + ty::TySlice(_) | ty::TyStr => { + let unit = t.sequence_element_type(bx.tcx()); + // The info in this case is the length of the str, so the size is that + // times the unit size. + let (size, align) = bx.cx.size_and_align_of(unit); + (bx.mul(info, C_usize(bx.cx, size.bytes())), + C_usize(bx.cx, align.abi())) + } + _ => { + let cx = bx.cx; + // First get the size of all statically known fields. + // Don't use size_of because it also rounds up to alignment, which we + // want to avoid, as the unsized field's alignment could be smaller. + assert!(!t.is_simd()); + let layout = cx.layout_of(t); + debug!("DST {} layout: {:?}", t, layout); + + let i = layout.fields.count() - 1; + let sized_size = layout.fields.offset(i).bytes(); + let sized_align = layout.align.abi(); + debug!("DST {} statically sized prefix size: {} align: {}", + t, sized_size, sized_align); + let sized_size = C_usize(cx, sized_size); + let sized_align = C_usize(cx, sized_align); + + // Recurse to get the size of the dynamically sized field (must be + // the last field). + let field_ty = layout.field(cx, i).ty; + let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); + + // FIXME (#26403, #27023): We should be adding padding + // to `sized_size` (to accommodate the `unsized_align` + // required of the unsized field that follows) before + // summing it with `sized_size`. (Note that since #26403 + // is unfixed, we do not yet add the necessary padding + // here. But this is where the add would go.) + + // Return the sum of sizes and max of aligns. + let size = bx.add(sized_size, unsized_size); + + // Packed types ignore the alignment of their fields. + if let ty::TyAdt(def, _) = t.sty { + if def.repr.packed() { + unsized_align = sized_align; + } + } + + // Choose max of two known alignments (combined value must + // be aligned according to more restrictive of the two). + let align = match (const_to_opt_u128(sized_align, false), + const_to_opt_u128(unsized_align, false)) { + (Some(sized_align), Some(unsized_align)) => { + // If both alignments are constant, (the sized_align should always be), then + // pick the correct alignment statically. + C_usize(cx, std::cmp::max(sized_align, unsized_align) as u64) + } + _ => bx.select(bx.icmp(llvm::IntUGT, sized_align, unsized_align), + sized_align, + unsized_align) + }; + + // Issue #27023: must add any necessary padding to `size` + // (to make it a multiple of `align`) before returning it. + // + // Namely, the returned size should be, in C notation: + // + // `size + ((size & (align-1)) ? align : 0)` + // + // emulated via the semi-standard fast bit trick: + // + // `(size + (align-1)) & -align` + + let addend = bx.sub(align, C_usize(bx.cx, 1)); + let size = bx.and(bx.add(size, addend), bx.neg(align)); + + (size, align) + } + } +} diff --git a/src/librustc_codegen_llvm/intrinsic.rs b/src/librustc_codegen_llvm/intrinsic.rs new file mode 100644 index 00000000000..ba04cc7fad5 --- /dev/null +++ b/src/librustc_codegen_llvm/intrinsic.rs @@ -0,0 +1,1465 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use intrinsics::{self, Intrinsic}; +use llvm; +use llvm::{ValueRef}; +use abi::{Abi, FnType, LlvmType, PassMode}; +use mir::place::PlaceRef; +use mir::operand::{OperandRef, OperandValue}; +use base::*; +use common::*; +use declare; +use glue; +use type_::Type; +use type_of::LayoutLlvmExt; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{HasDataLayout, LayoutOf}; +use rustc::hir; +use syntax::ast; +use syntax::symbol::Symbol; +use builder::Builder; + +use rustc::session::Session; +use syntax_pos::Span; + +use std::cmp::Ordering; +use std::iter; + +fn get_simple_intrinsic(cx: &CodegenCx, name: &str) -> Option { + let llvm_name = match name { + "sqrtf32" => "llvm.sqrt.f32", + "sqrtf64" => "llvm.sqrt.f64", + "powif32" => "llvm.powi.f32", + "powif64" => "llvm.powi.f64", + "sinf32" => "llvm.sin.f32", + "sinf64" => "llvm.sin.f64", + "cosf32" => "llvm.cos.f32", + "cosf64" => "llvm.cos.f64", + "powf32" => "llvm.pow.f32", + "powf64" => "llvm.pow.f64", + "expf32" => "llvm.exp.f32", + "expf64" => "llvm.exp.f64", + "exp2f32" => "llvm.exp2.f32", + "exp2f64" => "llvm.exp2.f64", + "logf32" => "llvm.log.f32", + "logf64" => "llvm.log.f64", + "log10f32" => "llvm.log10.f32", + "log10f64" => "llvm.log10.f64", + "log2f32" => "llvm.log2.f32", + "log2f64" => "llvm.log2.f64", + "fmaf32" => "llvm.fma.f32", + "fmaf64" => "llvm.fma.f64", + "fabsf32" => "llvm.fabs.f32", + "fabsf64" => "llvm.fabs.f64", + "copysignf32" => "llvm.copysign.f32", + "copysignf64" => "llvm.copysign.f64", + "floorf32" => "llvm.floor.f32", + "floorf64" => "llvm.floor.f64", + "ceilf32" => "llvm.ceil.f32", + "ceilf64" => "llvm.ceil.f64", + "truncf32" => "llvm.trunc.f32", + "truncf64" => "llvm.trunc.f64", + "rintf32" => "llvm.rint.f32", + "rintf64" => "llvm.rint.f64", + "nearbyintf32" => "llvm.nearbyint.f32", + "nearbyintf64" => "llvm.nearbyint.f64", + "roundf32" => "llvm.round.f32", + "roundf64" => "llvm.round.f64", + "assume" => "llvm.assume", + "abort" => "llvm.trap", + _ => return None + }; + Some(cx.get_intrinsic(&llvm_name)) +} + +/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, +/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, +/// add them to librustc_codegen_llvm/context.rs +pub fn codegen_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + callee_ty: Ty<'tcx>, + fn_ty: &FnType<'tcx, Ty<'tcx>>, + args: &[OperandRef<'tcx>], + llresult: ValueRef, + span: Span) { + let cx = bx.cx; + let tcx = cx.tcx; + + let (def_id, substs) = match callee_ty.sty { + ty::TyFnDef(def_id, substs) => (def_id, substs), + _ => bug!("expected fn item type, found {}", callee_ty) + }; + + let sig = callee_ty.fn_sig(tcx); + let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); + let arg_tys = sig.inputs(); + let ret_ty = sig.output(); + let name = &*tcx.item_name(def_id).as_str(); + + let llret_ty = cx.layout_of(ret_ty).llvm_type(cx); + let result = PlaceRef::new_sized(llresult, fn_ty.ret.layout, fn_ty.ret.layout.align); + + let simple = get_simple_intrinsic(cx, name); + let llval = match name { + _ if simple.is_some() => { + bx.call(simple.unwrap(), + &args.iter().map(|arg| arg.immediate()).collect::>(), + None) + } + "unreachable" => { + return; + }, + "likely" => { + let expect = cx.get_intrinsic(&("llvm.expect.i1")); + bx.call(expect, &[args[0].immediate(), C_bool(cx, true)], None) + } + "unlikely" => { + let expect = cx.get_intrinsic(&("llvm.expect.i1")); + bx.call(expect, &[args[0].immediate(), C_bool(cx, false)], None) + } + "try" => { + try_intrinsic(bx, cx, + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + llresult); + return; + } + "breakpoint" => { + let llfn = cx.get_intrinsic(&("llvm.debugtrap")); + bx.call(llfn, &[], None) + } + "size_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.size_of(tp_ty).bytes()) + } + "size_of_val" => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (llsize, _) = + glue::size_and_align_of_dst(bx, tp_ty, meta); + llsize + } else { + C_usize(cx, cx.size_of(tp_ty).bytes()) + } + } + "min_align_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.align_of(tp_ty).abi()) + } + "min_align_of_val" => { + let tp_ty = substs.type_at(0); + if let OperandValue::Pair(_, meta) = args[0].val { + let (_, llalign) = + glue::size_and_align_of_dst(bx, tp_ty, meta); + llalign + } else { + C_usize(cx, cx.align_of(tp_ty).abi()) + } + } + "pref_align_of" => { + let tp_ty = substs.type_at(0); + C_usize(cx, cx.align_of(tp_ty).pref()) + } + "type_name" => { + let tp_ty = substs.type_at(0); + let ty_name = Symbol::intern(&tp_ty.to_string()).as_str(); + C_str_slice(cx, ty_name) + } + "type_id" => { + C_u64(cx, cx.tcx.type_id_hash(substs.type_at(0))) + } + "init" => { + let ty = substs.type_at(0); + if !cx.layout_of(ty).is_zst() { + // Just zero out the stack slot. + // If we store a zero constant, LLVM will drown in vreg allocation for large data + // structures, and the generated code will be awful. (A telltale sign of this is + // large quantities of `mov [byte ptr foo],0` in the generated code.) + memset_intrinsic(bx, false, ty, llresult, C_u8(cx, 0), C_usize(cx, 1)); + } + return; + } + // Effectively no-ops + "uninit" => { + return; + } + "needs_drop" => { + let tp_ty = substs.type_at(0); + + C_bool(cx, bx.cx.type_needs_drop(tp_ty)) + } + "offset" => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.inbounds_gep(ptr, &[offset]) + } + "arith_offset" => { + let ptr = args[0].immediate(); + let offset = args[1].immediate(); + bx.gep(ptr, &[offset]) + } + + "copy_nonoverlapping" => { + copy_intrinsic(bx, false, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) + } + "copy" => { + copy_intrinsic(bx, true, false, substs.type_at(0), + args[1].immediate(), args[0].immediate(), args[2].immediate()) + } + "write_bytes" => { + memset_intrinsic(bx, false, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + + "volatile_copy_nonoverlapping_memory" => { + copy_intrinsic(bx, false, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_copy_memory" => { + copy_intrinsic(bx, true, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_set_memory" => { + memset_intrinsic(bx, true, substs.type_at(0), + args[0].immediate(), args[1].immediate(), args[2].immediate()) + } + "volatile_load" => { + let tp_ty = substs.type_at(0); + let mut ptr = args[0].immediate(); + if let PassMode::Cast(ty) = fn_ty.ret.mode { + ptr = bx.pointercast(ptr, ty.llvm_type(cx).ptr_to()); + } + let load = bx.volatile_load(ptr); + unsafe { + llvm::LLVMSetAlignment(load, cx.align_of(tp_ty).abi() as u32); + } + to_immediate(bx, load, cx.layout_of(tp_ty)) + }, + "volatile_store" => { + let dst = args[0].deref(bx.cx); + args[1].val.volatile_store(bx, dst); + return; + }, + "prefetch_read_data" | "prefetch_write_data" | + "prefetch_read_instruction" | "prefetch_write_instruction" => { + let expect = cx.get_intrinsic(&("llvm.prefetch")); + let (rw, cache_type) = match name { + "prefetch_read_data" => (0, 1), + "prefetch_write_data" => (1, 1), + "prefetch_read_instruction" => (0, 0), + "prefetch_write_instruction" => (1, 0), + _ => bug!() + }; + bx.call(expect, &[ + args[0].immediate(), + C_i32(cx, rw), + args[1].immediate(), + C_i32(cx, cache_type) + ], None) + }, + "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | + "bitreverse" | "add_with_overflow" | "sub_with_overflow" | + "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | + "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { + let ty = arg_tys[0]; + match int_type_width_signed(ty, cx) { + Some((width, signed)) => + match name { + "ctlz" | "cttz" => { + let y = C_bool(bx.cx, false); + let llfn = cx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); + bx.call(llfn, &[args[0].immediate(), y], None) + } + "ctlz_nonzero" | "cttz_nonzero" => { + let y = C_bool(bx.cx, true); + let llvm_name = &format!("llvm.{}.i{}", &name[..4], width); + let llfn = cx.get_intrinsic(llvm_name); + bx.call(llfn, &[args[0].immediate(), y], None) + } + "ctpop" => bx.call(cx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), + &[args[0].immediate()], None), + "bswap" => { + if width == 8 { + args[0].immediate() // byte swap a u8/i8 is just a no-op + } else { + bx.call(cx.get_intrinsic(&format!("llvm.bswap.i{}", width)), + &[args[0].immediate()], None) + } + } + "bitreverse" => { + bx.call(cx.get_intrinsic(&format!("llvm.bitreverse.i{}", width)), + &[args[0].immediate()], None) + } + "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { + let intrinsic = format!("llvm.{}{}.with.overflow.i{}", + if signed { 's' } else { 'u' }, + &name[..3], width); + let llfn = bx.cx.get_intrinsic(&intrinsic); + + // Convert `i1` to a `bool`, and write it to the out parameter + let pair = bx.call(llfn, &[ + args[0].immediate(), + args[1].immediate() + ], None); + let val = bx.extract_value(pair, 0); + let overflow = bx.zext(bx.extract_value(pair, 1), Type::bool(cx)); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(overflow, dest.llval, dest.align); + + return; + }, + "overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()), + "overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()), + "overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()), + "exact_div" => + if signed { + bx.exactsdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.exactudiv(args[0].immediate(), args[1].immediate()) + }, + "unchecked_div" => + if signed { + bx.sdiv(args[0].immediate(), args[1].immediate()) + } else { + bx.udiv(args[0].immediate(), args[1].immediate()) + }, + "unchecked_rem" => + if signed { + bx.srem(args[0].immediate(), args[1].immediate()) + } else { + bx.urem(args[0].immediate(), args[1].immediate()) + }, + "unchecked_shl" => bx.shl(args[0].immediate(), args[1].immediate()), + "unchecked_shr" => + if signed { + bx.ashr(args[0].immediate(), args[1].immediate()) + } else { + bx.lshr(args[0].immediate(), args[1].immediate()) + }, + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, ty)); + return; + } + } + + }, + "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { + let sty = &arg_tys[0].sty; + match float_type_width(sty) { + Some(_width) => + match name { + "fadd_fast" => bx.fadd_fast(args[0].immediate(), args[1].immediate()), + "fsub_fast" => bx.fsub_fast(args[0].immediate(), args[1].immediate()), + "fmul_fast" => bx.fmul_fast(args[0].immediate(), args[1].immediate()), + "fdiv_fast" => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), + "frem_fast" => bx.frem_fast(args[0].immediate(), args[1].immediate()), + _ => bug!(), + }, + None => { + span_invalid_monomorphization_error( + tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic float type, found `{}`", name, sty)); + return; + } + } + + }, + + "discriminant_value" => { + args[0].deref(bx.cx).codegen_get_discr(bx, ret_ty) + } + + "align_offset" => { + // `ptr as usize` + let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty); + // `ptr_val % align` + let align = args[1].immediate(); + let offset = bx.urem(ptr_val, align); + let zero = C_null(bx.cx.isize_ty); + // `offset == 0` + let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero); + // `if offset == 0 { 0 } else { align - offset }` + bx.select(is_zero, zero, bx.sub(align, offset)) + } + name if name.starts_with("simd_") => { + match generic_simd_intrinsic(bx, name, + callee_ty, + args, + ret_ty, llret_ty, + span) { + Ok(llval) => llval, + Err(()) => return + } + } + // This requires that atomic intrinsics follow a specific naming pattern: + // "atomic_[_]", and no ordering means SeqCst + name if name.starts_with("atomic_") => { + use llvm::AtomicOrdering::*; + + let split: Vec<&str> = name.split('_').collect(); + + let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; + let (order, failorder) = match split.len() { + 2 => (SequentiallyConsistent, SequentiallyConsistent), + 3 => match split[2] { + "unordered" => (Unordered, Unordered), + "relaxed" => (Monotonic, Monotonic), + "acq" => (Acquire, Acquire), + "rel" => (Release, Monotonic), + "acqrel" => (AcquireRelease, Acquire), + "failrelaxed" if is_cxchg => + (SequentiallyConsistent, Monotonic), + "failacq" if is_cxchg => + (SequentiallyConsistent, Acquire), + _ => cx.sess().fatal("unknown ordering in atomic intrinsic") + }, + 4 => match (split[2], split[3]) { + ("acq", "failrelaxed") if is_cxchg => + (Acquire, Monotonic), + ("acqrel", "failrelaxed") if is_cxchg => + (AcquireRelease, Monotonic), + _ => cx.sess().fatal("unknown ordering in atomic intrinsic") + }, + _ => cx.sess().fatal("Atomic intrinsic not in correct format"), + }; + + let invalid_monomorphization = |ty| { + span_invalid_monomorphization_error(tcx.sess, span, + &format!("invalid monomorphization of `{}` intrinsic: \ + expected basic integer type, found `{}`", name, ty)); + }; + + match split[1] { + "cxchg" | "cxchgweak" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; + let pair = bx.atomic_cmpxchg( + args[0].immediate(), + args[1].immediate(), + args[2].immediate(), + order, + failorder, + weak); + let val = bx.extract_value(pair, 0); + let success = bx.zext(bx.extract_value(pair, 1), Type::bool(bx.cx)); + + let dest = result.project_field(bx, 0); + bx.store(val, dest.llval, dest.align); + let dest = result.project_field(bx, 1); + bx.store(success, dest.llval, dest.align); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "load" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let align = cx.align_of(ty); + bx.atomic_load(args[0].immediate(), order, align) + } else { + return invalid_monomorphization(ty); + } + } + + "store" => { + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + let align = cx.align_of(ty); + bx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); + return; + } else { + return invalid_monomorphization(ty); + } + } + + "fence" => { + bx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); + return; + } + + "singlethreadfence" => { + bx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); + return; + } + + // These are all AtomicRMW ops + op => { + let atom_op = match op { + "xchg" => llvm::AtomicXchg, + "xadd" => llvm::AtomicAdd, + "xsub" => llvm::AtomicSub, + "and" => llvm::AtomicAnd, + "nand" => llvm::AtomicNand, + "or" => llvm::AtomicOr, + "xor" => llvm::AtomicXor, + "max" => llvm::AtomicMax, + "min" => llvm::AtomicMin, + "umax" => llvm::AtomicUMax, + "umin" => llvm::AtomicUMin, + _ => cx.sess().fatal("unknown atomic operation") + }; + + let ty = substs.type_at(0); + if int_type_width_signed(ty, cx).is_some() { + bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) + } else { + return invalid_monomorphization(ty); + } + } + } + } + + "nontemporal_store" => { + let dst = args[0].deref(bx.cx); + args[1].val.nontemporal_store(bx, dst); + return; + } + + _ => { + let intr = match Intrinsic::find(&name) { + Some(intr) => intr, + None => bug!("unknown intrinsic '{}'", name), + }; + fn one(x: Vec) -> T { + assert_eq!(x.len(), 1); + x.into_iter().next().unwrap() + } + fn ty_to_type(cx: &CodegenCx, t: &intrinsics::Type) -> Vec { + use intrinsics::Type::*; + match *t { + Void => vec![Type::void(cx)], + Integer(_signed, _width, llvm_width) => { + vec![Type::ix(cx, llvm_width as u64)] + } + Float(x) => { + match x { + 32 => vec![Type::f32(cx)], + 64 => vec![Type::f64(cx)], + _ => bug!() + } + } + Pointer(ref t, ref llvm_elem, _const) => { + let t = llvm_elem.as_ref().unwrap_or(t); + let elem = one(ty_to_type(cx, t)); + vec![elem.ptr_to()] + } + Vector(ref t, ref llvm_elem, length) => { + let t = llvm_elem.as_ref().unwrap_or(t); + let elem = one(ty_to_type(cx, t)); + vec![Type::vector(&elem, length as u64)] + } + Aggregate(false, ref contents) => { + let elems = contents.iter() + .map(|t| one(ty_to_type(cx, t))) + .collect::>(); + vec![Type::struct_(cx, &elems, false)] + } + Aggregate(true, ref contents) => { + contents.iter() + .flat_map(|t| ty_to_type(cx, t)) + .collect() + } + } + } + + // This allows an argument list like `foo, (bar, baz), + // qux` to be converted into `foo, bar, baz, qux`, integer + // arguments to be truncated as needed and pointers to be + // cast. + fn modify_as_needed<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + t: &intrinsics::Type, + arg: &OperandRef<'tcx>) + -> Vec + { + match *t { + intrinsics::Type::Aggregate(true, ref contents) => { + // We found a tuple that needs squishing! So + // run over the tuple and load each field. + // + // This assumes the type is "simple", i.e. no + // destructors, and the contents are SIMD + // etc. + assert!(!bx.cx.type_needs_drop(arg.layout.ty)); + let (ptr, align) = match arg.val { + OperandValue::Ref(ptr, align) => (ptr, align), + _ => bug!() + }; + let arg = PlaceRef::new_sized(ptr, arg.layout, align); + (0..contents.len()).map(|i| { + arg.project_field(bx, i).load(bx).immediate() + }).collect() + } + intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { + let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); + vec![bx.pointercast(arg.immediate(), llvm_elem.ptr_to())] + } + intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { + let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); + vec![bx.bitcast(arg.immediate(), Type::vector(&llvm_elem, length as u64))] + } + intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { + // the LLVM intrinsic uses a smaller integer + // size than the C intrinsic's signature, so + // we have to trim it down here. + vec![bx.trunc(arg.immediate(), Type::ix(bx.cx, llvm_width as u64))] + } + _ => vec![arg.immediate()], + } + } + + + let inputs = intr.inputs.iter() + .flat_map(|t| ty_to_type(cx, t)) + .collect::>(); + + let outputs = one(ty_to_type(cx, &intr.output)); + + let llargs: Vec<_> = intr.inputs.iter().zip(args).flat_map(|(t, arg)| { + modify_as_needed(bx, t, arg) + }).collect(); + assert_eq!(inputs.len(), llargs.len()); + + let val = match intr.definition { + intrinsics::IntrinsicDef::Named(name) => { + let f = declare::declare_cfn(cx, + name, + Type::func(&inputs, &outputs)); + bx.call(f, &llargs, None) + } + }; + + match *intr.output { + intrinsics::Type::Aggregate(flatten, ref elems) => { + // the output is a tuple so we need to munge it properly + assert!(!flatten); + + for i in 0..elems.len() { + let dest = result.project_field(bx, i); + let val = bx.extract_value(val, i as u64); + bx.store(val, dest.llval, dest.align); + } + return; + } + _ => val, + } + } + }; + + if !fn_ty.ret.is_ignore() { + if let PassMode::Cast(ty) = fn_ty.ret.mode { + let ptr = bx.pointercast(result.llval, ty.llvm_type(cx).ptr_to()); + bx.store(llval, ptr, result.align); + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) + .val.store(bx, result); + } + } +} + +fn copy_intrinsic<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + allow_overlap: bool, + volatile: bool, + ty: Ty<'tcx>, + dst: ValueRef, + src: ValueRef, + count: ValueRef) + -> ValueRef { + let cx = bx.cx; + let (size, align) = cx.size_and_align_of(ty); + let size = C_usize(cx, size.bytes()); + let align = C_i32(cx, align.abi() as i32); + + let operation = if allow_overlap { + "memmove" + } else { + "memcpy" + }; + + let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, + cx.data_layout().pointer_size.bits()); + + let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); + let src_ptr = bx.pointercast(src, Type::i8p(cx)); + let llfn = cx.get_intrinsic(&name); + + bx.call(llfn, + &[dst_ptr, + src_ptr, + bx.mul(size, count), + align, + C_bool(cx, volatile)], + None) +} + +fn memset_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + volatile: bool, + ty: Ty<'tcx>, + dst: ValueRef, + val: ValueRef, + count: ValueRef +) -> ValueRef { + let cx = bx.cx; + let (size, align) = cx.size_and_align_of(ty); + let size = C_usize(cx, size.bytes()); + let align = C_i32(cx, align.abi() as i32); + let dst = bx.pointercast(dst, Type::i8p(cx)); + call_memset(bx, dst, val, bx.mul(size, count), align, volatile) +} + +fn try_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef, +) { + if bx.sess().no_landing_pads() { + bx.call(func, &[data], None); + let ptr_align = bx.tcx().data_layout.pointer_align; + bx.store(C_null(Type::i8p(&bx.cx)), dest, ptr_align); + } else if wants_msvc_seh(bx.sess()) { + codegen_msvc_try(bx, cx, func, data, local_ptr, dest); + } else { + codegen_gnu_try(bx, cx, func, data, local_ptr, dest); + } +} + +// MSVC's definition of the `rust_try` function. +// +// This implementation uses the new exception handling instructions in LLVM +// which have support in LLVM for SEH on MSVC targets. Although these +// instructions are meant to work for all targets, as of the time of this +// writing, however, LLVM does not recommend the usage of these new instructions +// as the old ones are still more optimized. +fn codegen_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(cx, &mut |bx| { + let cx = bx.cx; + + bx.set_personality_fn(bx.cx.eh_personality()); + + let normal = bx.build_sibling_block("normal"); + let catchswitch = bx.build_sibling_block("catchswitch"); + let catchpad = bx.build_sibling_block("catchpad"); + let caught = bx.build_sibling_block("caught"); + + let func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let local_ptr = llvm::get_param(bx.llfn(), 2); + + // We're generating an IR snippet that looks like: + // + // declare i32 @rust_try(%func, %data, %ptr) { + // %slot = alloca i64* + // invoke %func(%data) to label %normal unwind label %catchswitch + // + // normal: + // ret i32 0 + // + // catchswitch: + // %cs = catchswitch within none [%catchpad] unwind to caller + // + // catchpad: + // %tok = catchpad within %cs [%type_descriptor, 0, %slot] + // %ptr[0] = %slot[0] + // %ptr[1] = %slot[1] + // catchret from %tok to label %caught + // + // caught: + // ret i32 1 + // } + // + // This structure follows the basic usage of throw/try/catch in LLVM. + // For example, compile this C++ snippet to see what LLVM generates: + // + // #include + // + // int bar(void (*foo)(void), uint64_t *ret) { + // try { + // foo(); + // return 0; + // } catch(uint64_t a[2]) { + // ret[0] = a[0]; + // ret[1] = a[1]; + // return 1; + // } + // } + // + // More information can be found in libstd's seh.rs implementation. + let i64p = Type::i64(cx).ptr_to(); + let ptr_align = bx.tcx().data_layout.pointer_align; + let slot = bx.alloca(i64p, "slot", ptr_align); + bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), + None); + + normal.ret(C_i32(cx, 0)); + + let cs = catchswitch.catch_switch(None, None, 1); + catchswitch.add_handler(cs, catchpad.llbb()); + + let tcx = cx.tcx; + let tydesc = match tcx.lang_items().msvc_try_filter() { + Some(did) => ::consts::get_static(cx, did), + None => bug!("msvc_try_filter not defined"), + }; + let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(cx, 0), slot]); + let addr = catchpad.load(slot, ptr_align); + + let i64_align = bx.tcx().data_layout.i64_align; + let arg1 = catchpad.load(addr, i64_align); + let val1 = C_i32(cx, 1); + let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), i64_align); + let local_ptr = catchpad.bitcast(local_ptr, i64p); + catchpad.store(arg1, local_ptr, i64_align); + catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), i64_align); + catchpad.catch_ret(tok, caught.llbb()); + + caught.ret(C_i32(cx, 1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[func, data, local_ptr], None); + let i32_align = bx.tcx().data_layout.i32_align; + bx.store(ret, dest, i32_align); +} + +// Definition of the standard "try" function for Rust using the GNU-like model +// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke +// instructions). +// +// This codegen is a little surprising because we always call a shim +// function instead of inlining the call to `invoke` manually here. This is done +// because in LLVM we're only allowed to have one personality per function +// definition. The call to the `try` intrinsic is being inlined into the +// function calling it, and that function may already have other personality +// functions in play. By calling a shim we're guaranteed that our shim will have +// the right personality function. +fn codegen_gnu_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + cx: &CodegenCx, + func: ValueRef, + data: ValueRef, + local_ptr: ValueRef, + dest: ValueRef) { + let llfn = get_rust_try_fn(cx, &mut |bx| { + let cx = bx.cx; + + // Codegens the shims described above: + // + // bx: + // invoke %func(%args...) normal %normal unwind %catch + // + // normal: + // ret 0 + // + // catch: + // (ptr, _) = landingpad + // store ptr, %local_ptr + // ret 1 + // + // Note that the `local_ptr` data passed into the `try` intrinsic is + // expected to be `*mut *mut u8` for this to actually work, but that's + // managed by the standard library. + + let then = bx.build_sibling_block("then"); + let catch = bx.build_sibling_block("catch"); + + let func = llvm::get_param(bx.llfn(), 0); + let data = llvm::get_param(bx.llfn(), 1); + let local_ptr = llvm::get_param(bx.llfn(), 2); + bx.invoke(func, &[data], then.llbb(), catch.llbb(), None); + then.ret(C_i32(cx, 0)); + + // Type indicator for the exception being thrown. + // + // The first value in this tuple is a pointer to the exception object + // being thrown. The second value is a "selector" indicating which of + // the landing pad clauses the exception's type had been matched to. + // rust_try ignores the selector. + let lpad_ty = Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], + false); + let vals = catch.landing_pad(lpad_ty, bx.cx.eh_personality(), 1); + catch.add_clause(vals, C_null(Type::i8p(cx))); + let ptr = catch.extract_value(vals, 0); + let ptr_align = bx.tcx().data_layout.pointer_align; + catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(cx).ptr_to()), ptr_align); + catch.ret(C_i32(cx, 1)); + }); + + // Note that no invoke is used here because by definition this function + // can't panic (that's what it's catching). + let ret = bx.call(llfn, &[func, data, local_ptr], None); + let i32_align = bx.tcx().data_layout.i32_align; + bx.store(ret, dest, i32_align); +} + +// Helper function to give a Block to a closure to codegen a shim function. +// This is currently primarily used for the `try` intrinsic functions above. +fn gen_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + name: &str, + inputs: Vec>, + output: Ty<'tcx>, + codegen: &mut for<'b> FnMut(Builder<'b, 'tcx>)) + -> ValueRef { + let rust_fn_ty = cx.tcx.mk_fn_ptr(ty::Binder::bind(cx.tcx.mk_fn_sig( + inputs.into_iter(), + output, + false, + hir::Unsafety::Unsafe, + Abi::Rust + ))); + let llfn = declare::define_internal_fn(cx, name, rust_fn_ty); + let bx = Builder::new_block(cx, llfn, "entry-block"); + codegen(bx); + llfn +} + +// Helper function used to get a handle to the `__rust_try` function used to +// catch exceptions. +// +// This function is only generated once and is then cached. +fn get_rust_try_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + codegen: &mut for<'b> FnMut(Builder<'b, 'tcx>)) + -> ValueRef { + if let Some(llfn) = cx.rust_try_fn.get() { + return llfn; + } + + // Define the type up front for the signature of the rust_try function. + let tcx = cx.tcx; + let i8p = tcx.mk_mut_ptr(tcx.types.i8); + let fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( + iter::once(i8p), + tcx.mk_nil(), + false, + hir::Unsafety::Unsafe, + Abi::Rust + ))); + let output = tcx.types.i32; + let rust_try = gen_fn(cx, "__rust_try", vec![fn_ty, i8p, i8p], output, codegen); + cx.rust_try_fn.set(Some(rust_try)); + return rust_try +} + +fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { + span_err!(a, b, E0511, "{}", c); +} + +fn generic_simd_intrinsic<'a, 'tcx>( + bx: &Builder<'a, 'tcx>, + name: &str, + callee_ty: Ty<'tcx>, + args: &[OperandRef<'tcx>], + ret_ty: Ty<'tcx>, + llret_ty: Type, + span: Span +) -> Result { + // macros for error handling: + macro_rules! emit_error { + ($msg: tt) => { + emit_error!($msg, ) + }; + ($msg: tt, $($fmt: tt)*) => { + span_invalid_monomorphization_error( + bx.sess(), span, + &format!(concat!("invalid monomorphization of `{}` intrinsic: ", + $msg), + name, $($fmt)*)); + } + } + macro_rules! return_error { + ($($fmt: tt)*) => { + { + emit_error!($($fmt)*); + return Err(()); + } + } + } + + macro_rules! require { + ($cond: expr, $($fmt: tt)*) => { + if !$cond { + return_error!($($fmt)*); + } + }; + } + macro_rules! require_simd { + ($ty: expr, $position: expr) => { + require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) + } + } + + + + let tcx = bx.tcx(); + let sig = tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &callee_ty.fn_sig(tcx), + ); + let arg_tys = sig.inputs(); + + // every intrinsic takes a SIMD vector as its first argument + require_simd!(arg_tys[0], "input"); + let in_ty = arg_tys[0]; + let in_elem = arg_tys[0].simd_type(tcx); + let in_len = arg_tys[0].simd_size(tcx); + + let comparison = match name { + "simd_eq" => Some(hir::BiEq), + "simd_ne" => Some(hir::BiNe), + "simd_lt" => Some(hir::BiLt), + "simd_le" => Some(hir::BiLe), + "simd_gt" => Some(hir::BiGt), + "simd_ge" => Some(hir::BiGe), + _ => None + }; + + if let Some(cmp_op) = comparison { + require_simd!(ret_ty, "return"); + + let out_len = ret_ty.simd_size(tcx); + require!(in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, in_ty, + ret_ty, out_len); + require!(llret_ty.element_type().kind() == llvm::Integer, + "expected return type with integer elements, found `{}` with non-integer `{}`", + ret_ty, + ret_ty.simd_type(tcx)); + + return Ok(compare_simd_types(bx, + args[0].immediate(), + args[1].immediate(), + in_elem, + llret_ty, + cmp_op)) + } + + if name.starts_with("simd_shuffle") { + let n: usize = match name["simd_shuffle".len()..].parse() { + Ok(n) => n, + Err(_) => span_bug!(span, + "bad `simd_shuffle` instruction only caught in codegen?") + }; + + require_simd!(ret_ty, "return"); + + let out_len = ret_ty.simd_size(tcx); + require!(out_len == n, + "expected return type of length {}, found `{}` with length {}", + n, ret_ty, out_len); + require!(in_elem == ret_ty.simd_type(tcx), + "expected return element type `{}` (element of input `{}`), \ + found `{}` with element type `{}`", + in_elem, in_ty, + ret_ty, ret_ty.simd_type(tcx)); + + let total_len = in_len as u128 * 2; + + let vector = args[2].immediate(); + + let indices: Option> = (0..n) + .map(|i| { + let arg_idx = i; + let val = const_get_elt(vector, i as u64); + match const_to_opt_u128(val, true) { + None => { + emit_error!("shuffle index #{} is not a constant", arg_idx); + None + } + Some(idx) if idx >= total_len => { + emit_error!("shuffle index #{} is out of bounds (limit {})", + arg_idx, total_len); + None + } + Some(idx) => Some(C_i32(bx.cx, idx as i32)), + } + }) + .collect(); + let indices = match indices { + Some(i) => i, + None => return Ok(C_null(llret_ty)) + }; + + return Ok(bx.shuffle_vector(args[0].immediate(), + args[1].immediate(), + C_vector(&indices))) + } + + if name == "simd_insert" { + require!(in_elem == arg_tys[2], + "expected inserted type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, arg_tys[2]); + return Ok(bx.insert_element(args[0].immediate(), + args[2].immediate(), + args[1].immediate())) + } + if name == "simd_extract" { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())) + } + + if name == "simd_select" { + let m_elem_ty = in_elem; + let m_len = in_len; + let v_len = arg_tys[1].simd_size(tcx); + require!(m_len == v_len, + "mismatched lengths: mask length `{}` != other vector length `{}`", + m_len, v_len + ); + match m_elem_ty.sty { + ty::TyInt(_) => {}, + _ => { + return_error!("mask element type is `{}`, expected `i_`", m_elem_ty); + } + } + // truncate the mask to a vector of i1s + let i1 = Type::i1(bx.cx); + let i1xn = Type::vector(&i1, m_len as u64); + let m_i1s = bx.trunc(args[0].immediate(), i1xn); + return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); + } + + macro_rules! arith_red { + ($name:tt : $integer_reduce:ident, $float_reduce:ident, $ordered:expr) => { + if name == $name { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => { + let r = bx.$integer_reduce(args[0].immediate()); + if $ordered { + // if overflow occurs, the result is the + // mathematical result modulo 2^n: + if name.contains("mul") { + Ok(bx.mul(args[1].immediate(), r)) + } else { + Ok(bx.add(args[1].immediate(), r)) + } + } else { + Ok(bx.$integer_reduce(args[0].immediate())) + } + }, + ty::TyFloat(f) => { + // ordered arithmetic reductions take an accumulator + let acc = if $ordered { + let acc = args[1].immediate(); + // FIXME: https://bugs.llvm.org/show_bug.cgi?id=36734 + // * if the accumulator of the fadd isn't 0, incorrect + // code is generated + // * if the accumulator of the fmul isn't 1, incorrect + // code is generated + match const_get_real(acc) { + None => return_error!("accumulator of {} is not a constant", $name), + Some((v, loses_info)) => { + if $name.contains("mul") && v != 1.0_f64 { + return_error!("accumulator of {} is not 1.0", $name); + } else if $name.contains("add") && v != 0.0_f64 { + return_error!("accumulator of {} is not 0.0", $name); + } else if loses_info { + return_error!("accumulator of {} loses information", $name); + } + } + } + acc + } else { + // unordered arithmetic reductions do not: + match f.bit_width() { + 32 => C_undef(Type::f32(bx.cx)), + 64 => C_undef(Type::f64(bx.cx)), + v => { + return_error!(r#" +unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, + $name, in_ty, in_elem, v, ret_ty + ) + } + } + + }; + Ok(bx.$float_reduce(acc, args[0].immediate())) + } + _ => { + return_error!( + "unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty + ) + }, + } + } + } + } + + arith_red!("simd_reduce_add_ordered": vector_reduce_add, vector_reduce_fadd_fast, true); + arith_red!("simd_reduce_mul_ordered": vector_reduce_mul, vector_reduce_fmul_fast, true); + arith_red!("simd_reduce_add_unordered": vector_reduce_add, vector_reduce_fadd_fast, false); + arith_red!("simd_reduce_mul_unordered": vector_reduce_mul, vector_reduce_fmul_fast, false); + + macro_rules! minmax_red { + ($name:tt: $int_red:ident, $float_red:ident) => { + if name == $name { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + return match in_elem.sty { + ty::TyInt(_i) => { + Ok(bx.$int_red(args[0].immediate(), true)) + }, + ty::TyUint(_u) => { + Ok(bx.$int_red(args[0].immediate(), false)) + }, + ty::TyFloat(_f) => { + Ok(bx.$float_red(args[0].immediate())) + } + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + }, + } + } + + } + } + + minmax_red!("simd_reduce_min": vector_reduce_min, vector_reduce_fmin); + minmax_red!("simd_reduce_max": vector_reduce_max, vector_reduce_fmax); + + minmax_red!("simd_reduce_min_nanless": vector_reduce_min, vector_reduce_fmin_fast); + minmax_red!("simd_reduce_max_nanless": vector_reduce_max, vector_reduce_fmax_fast); + + macro_rules! bitwise_red { + ($name:tt : $red:ident, $boolean:expr) => { + if name == $name { + let input = if !$boolean { + require!(ret_ty == in_elem, + "expected return type `{}` (element of input `{}`), found `{}`", + in_elem, in_ty, ret_ty); + args[0].immediate() + } else { + match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => {}, + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + } + } + + // boolean reductions operate on vectors of i1s: + let i1 = Type::i1(bx.cx); + let i1xn = Type::vector(&i1, in_len as u64); + bx.trunc(args[0].immediate(), i1xn) + }; + return match in_elem.sty { + ty::TyInt(_) | ty::TyUint(_) => { + let r = bx.$red(input); + Ok( + if !$boolean { + r + } else { + bx.zext(r, Type::bool(bx.cx)) + } + ) + }, + _ => { + return_error!("unsupported {} from `{}` with element `{}` to `{}`", + $name, in_ty, in_elem, ret_ty) + }, + } + } + } + } + + bitwise_red!("simd_reduce_and": vector_reduce_and, false); + bitwise_red!("simd_reduce_or": vector_reduce_or, false); + bitwise_red!("simd_reduce_xor": vector_reduce_xor, false); + bitwise_red!("simd_reduce_all": vector_reduce_and, true); + bitwise_red!("simd_reduce_any": vector_reduce_or, true); + + if name == "simd_cast" { + require_simd!(ret_ty, "return"); + let out_len = ret_ty.simd_size(tcx); + require!(in_len == out_len, + "expected return type with length {} (same as input type `{}`), \ + found `{}` with length {}", + in_len, in_ty, + ret_ty, out_len); + // casting cares about nominal type, not just structural type + let out_elem = ret_ty.simd_type(tcx); + + if in_elem == out_elem { return Ok(args[0].immediate()); } + + enum Style { Float, Int(/* is signed? */ bool), Unsupported } + + let (in_style, in_width) = match in_elem.sty { + // vectors of pointer-sized integers should've been + // disallowed before here, so this unwrap is safe. + ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::TyFloat(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0) + }; + let (out_style, out_width) = match out_elem.sty { + ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), + ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), + ty::TyFloat(f) => (Style::Float, f.bit_width()), + _ => (Style::Unsupported, 0) + }; + + match (in_style, out_style) { + (Style::Int(in_is_signed), Style::Int(_)) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => if in_is_signed { + bx.sext(args[0].immediate(), llret_ty) + } else { + bx.zext(args[0].immediate(), llret_ty) + } + }) + } + (Style::Int(in_is_signed), Style::Float) => { + return Ok(if in_is_signed { + bx.sitofp(args[0].immediate(), llret_ty) + } else { + bx.uitofp(args[0].immediate(), llret_ty) + }) + } + (Style::Float, Style::Int(out_is_signed)) => { + return Ok(if out_is_signed { + bx.fptosi(args[0].immediate(), llret_ty) + } else { + bx.fptoui(args[0].immediate(), llret_ty) + }) + } + (Style::Float, Style::Float) => { + return Ok(match in_width.cmp(&out_width) { + Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), + Ordering::Equal => args[0].immediate(), + Ordering::Less => bx.fpext(args[0].immediate(), llret_ty) + }) + } + _ => {/* Unsupported. Fallthrough. */} + } + require!(false, + "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", + in_ty, in_elem, + ret_ty, out_elem); + } + macro_rules! arith { + ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { + $(if name == stringify!($name) { + match in_elem.sty { + $($(ty::$p(_))|* => { + return Ok(bx.$call(args[0].immediate(), args[1].immediate())) + })* + _ => {}, + } + require!(false, + "unsupported operation on `{}` with element `{}`", + in_ty, + in_elem) + })* + } + } + arith! { + simd_add: TyUint, TyInt => add, TyFloat => fadd; + simd_sub: TyUint, TyInt => sub, TyFloat => fsub; + simd_mul: TyUint, TyInt => mul, TyFloat => fmul; + simd_div: TyUint => udiv, TyInt => sdiv, TyFloat => fdiv; + simd_rem: TyUint => urem, TyInt => srem, TyFloat => frem; + simd_shl: TyUint, TyInt => shl; + simd_shr: TyUint => lshr, TyInt => ashr; + simd_and: TyUint, TyInt => and; + simd_or: TyUint, TyInt => or; + simd_xor: TyUint, TyInt => xor; + simd_fmax: TyFloat => maxnum; + simd_fmin: TyFloat => minnum; + } + span_bug!(span, "unknown SIMD intrinsic"); +} + +// Returns the width of an int Ty, and if it's signed or not +// Returns None if the type is not an integer +// FIXME: there’s multiple of this functions, investigate using some of the already existing +// stuffs. +fn int_type_width_signed(ty: Ty, cx: &CodegenCx) -> Option<(u64, bool)> { + match ty.sty { + ty::TyInt(t) => Some((match t { + ast::IntTy::Isize => { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + tws => bug!("Unsupported target word size for isize: {}", tws), + } + }, + ast::IntTy::I8 => 8, + ast::IntTy::I16 => 16, + ast::IntTy::I32 => 32, + ast::IntTy::I64 => 64, + ast::IntTy::I128 => 128, + }, true)), + ty::TyUint(t) => Some((match t { + ast::UintTy::Usize => { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => 16, + "32" => 32, + "64" => 64, + tws => bug!("Unsupported target word size for usize: {}", tws), + } + }, + ast::UintTy::U8 => 8, + ast::UintTy::U16 => 16, + ast::UintTy::U32 => 32, + ast::UintTy::U64 => 64, + ast::UintTy::U128 => 128, + }, false)), + _ => None, + } +} + +// Returns the width of a float TypeVariant +// Returns None if the type is not a float +fn float_type_width<'tcx>(sty: &ty::TypeVariants<'tcx>) + -> Option { + use rustc::ty::TyFloat; + match *sty { + TyFloat(t) => Some(match t { + ast::FloatTy::F32 => 32, + ast::FloatTy::F64 => 64, + }), + _ => None, + } +} diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs new file mode 100644 index 00000000000..bd053da4bd3 --- /dev/null +++ b/src/librustc_codegen_llvm/lib.rs @@ -0,0 +1,392 @@ +// Copyright 2012-2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![feature(fs_read_write)] +#![allow(unused_attributes)] +#![feature(libc)] +#![feature(quote)] +#![feature(range_contains)] +#![feature(rustc_diagnostic_macros)] +#![feature(slice_sort_by_cached_key)] +#![feature(optin_builtin_traits)] +#![feature(inclusive_range_methods)] + +use rustc::dep_graph::WorkProduct; +use syntax_pos::symbol::Symbol; + +#[macro_use] extern crate bitflags; +extern crate flate2; +extern crate libc; +#[macro_use] extern crate rustc; +extern crate jobserver; +extern crate num_cpus; +extern crate rustc_mir; +extern crate rustc_allocator; +extern crate rustc_apfloat; +extern crate rustc_target; +#[macro_use] extern crate rustc_data_structures; +extern crate rustc_demangle; +extern crate rustc_incremental; +extern crate rustc_llvm as llvm; +extern crate rustc_platform_intrinsics as intrinsics; +extern crate rustc_codegen_utils; + +#[macro_use] extern crate log; +#[macro_use] extern crate syntax; +extern crate syntax_pos; +extern crate rustc_errors as errors; +extern crate serialize; +extern crate cc; // Used to locate MSVC +extern crate tempdir; + +use back::bytecode::RLIB_BYTECODE_EXTENSION; + +pub use llvm_util::target_features; + +use std::any::Any; +use std::path::PathBuf; +use std::sync::mpsc; +use std::collections::BTreeMap; +use rustc_data_structures::sync::Lrc; + +use rustc::dep_graph::DepGraph; +use rustc::hir::def_id::CrateNum; +use rustc::middle::cstore::MetadataLoader; +use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource}; +use rustc::middle::lang_items::LangItem; +use rustc::session::{Session, CompileIncomplete}; +use rustc::session::config::{OutputFilenames, OutputType, PrintRequest}; +use rustc::ty::{self, TyCtxt}; +use rustc::util::nodemap::{FxHashSet, FxHashMap}; +use rustc_mir::monomorphize; +use rustc_codegen_utils::codegen_backend::CodegenBackend; + +mod diagnostics; + +mod back { + pub use rustc_codegen_utils::symbol_names; + mod archive; + pub mod bytecode; + mod command; + pub mod linker; + pub mod link; + mod lto; + pub mod symbol_export; + pub mod write; + mod rpath; + mod wasm; +} + +mod abi; +mod allocator; +mod asm; +mod attributes; +mod base; +mod builder; +mod callee; +mod common; +mod consts; +mod context; +mod debuginfo; +mod declare; +mod glue; +mod intrinsic; +mod llvm_util; +mod metadata; +mod meth; +mod mir; +mod time_graph; +mod mono_item; +mod type_; +mod type_of; +mod value; + +pub struct LlvmCodegenBackend(()); + +impl !Send for LlvmCodegenBackend {} // Llvm is on a per-thread basis +impl !Sync for LlvmCodegenBackend {} + +impl LlvmCodegenBackend { + pub fn new() -> Box { + box LlvmCodegenBackend(()) + } +} + +impl CodegenBackend for LlvmCodegenBackend { + fn init(&self, sess: &Session) { + llvm_util::init(sess); // Make sure llvm is inited + } + + fn print(&self, req: PrintRequest, sess: &Session) { + match req { + PrintRequest::RelocationModels => { + println!("Available relocation models:"); + for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { + println!(" {}", name); + } + println!(""); + } + PrintRequest::CodeModels => { + println!("Available code models:"); + for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){ + println!(" {}", name); + } + println!(""); + } + PrintRequest::TlsModels => { + println!("Available TLS models:"); + for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){ + println!(" {}", name); + } + println!(""); + } + req => llvm_util::print(req, sess), + } + } + + fn print_passes(&self) { + llvm_util::print_passes(); + } + + fn print_version(&self) { + llvm_util::print_version(); + } + + fn diagnostics(&self) -> &[(&'static str, &'static str)] { + &DIAGNOSTICS + } + + fn target_features(&self, sess: &Session) -> Vec { + target_features(sess) + } + + fn metadata_loader(&self) -> Box { + box metadata::LlvmMetadataLoader + } + + fn provide(&self, providers: &mut ty::maps::Providers) { + back::symbol_names::provide(providers); + back::symbol_export::provide(providers); + base::provide(providers); + attributes::provide(providers); + } + + fn provide_extern(&self, providers: &mut ty::maps::Providers) { + back::symbol_export::provide_extern(providers); + base::provide_extern(providers); + attributes::provide_extern(providers); + } + + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Box { + box base::codegen_crate(tcx, rx) + } + + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete>{ + use rustc::util::common::time; + let (ongoing_codegen, work_products) = + ongoing_codegen.downcast::<::back::write::OngoingCodegen>() + .expect("Expected LlvmCodegenBackend's OngoingCodegen, found Box") + .join(sess); + if sess.opts.debugging_opts.incremental_info { + back::write::dump_incremental_data(&ongoing_codegen); + } + + time(sess, + "serialize work products", + move || rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)); + + sess.compile_status()?; + + if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe || + i == OutputType::Metadata) { + return Ok(()); + } + + // Run the linker on any artifacts that resulted from the LLVM run. + // This should produce either a finished executable or library. + time(sess, "linking", || { + back::link::link_binary(sess, &ongoing_codegen, + outputs, &ongoing_codegen.crate_name.as_str()); + }); + + // Now that we won't touch anything in the incremental compilation directory + // any more, we can finalize it (which involves renaming it) + rustc_incremental::finalize_session_directory(sess, ongoing_codegen.link.crate_hash); + + Ok(()) + } +} + +/// This is the entrypoint for a hot plugged rustc_codegen_llvm +#[no_mangle] +pub fn __rustc_codegen_backend() -> Box { + LlvmCodegenBackend::new() +} + +struct ModuleCodegen { + /// The name of the module. When the crate may be saved between + /// compilations, incremental compilation requires that name be + /// unique amongst **all** crates. Therefore, it should contain + /// something unique to this crate (e.g., a module path) as well + /// as the crate name and disambiguator. + name: String, + llmod_id: String, + source: ModuleSource, + kind: ModuleKind, +} + +#[derive(Copy, Clone, Debug, PartialEq)] +enum ModuleKind { + Regular, + Metadata, + Allocator, +} + +impl ModuleCodegen { + fn llvm(&self) -> Option<&ModuleLlvm> { + match self.source { + ModuleSource::Codegened(ref llvm) => Some(llvm), + ModuleSource::Preexisting(_) => None, + } + } + + fn into_compiled_module(self, + emit_obj: bool, + emit_bc: bool, + emit_bc_compressed: bool, + outputs: &OutputFilenames) -> CompiledModule { + let pre_existing = match self.source { + ModuleSource::Preexisting(_) => true, + ModuleSource::Codegened(_) => false, + }; + let object = if emit_obj { + Some(outputs.temp_path(OutputType::Object, Some(&self.name))) + } else { + None + }; + let bytecode = if emit_bc { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name))) + } else { + None + }; + let bytecode_compressed = if emit_bc_compressed { + Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name)) + .with_extension(RLIB_BYTECODE_EXTENSION)) + } else { + None + }; + + CompiledModule { + llmod_id: self.llmod_id, + name: self.name.clone(), + kind: self.kind, + pre_existing, + object, + bytecode, + bytecode_compressed, + } + } +} + +#[derive(Debug)] +struct CompiledModule { + name: String, + llmod_id: String, + kind: ModuleKind, + pre_existing: bool, + object: Option, + bytecode: Option, + bytecode_compressed: Option, +} + +enum ModuleSource { + /// Copy the `.o` files or whatever from the incr. comp. directory. + Preexisting(WorkProduct), + + /// Rebuild from this LLVM module. + Codegened(ModuleLlvm), +} + +#[derive(Debug)] +struct ModuleLlvm { + llcx: llvm::ContextRef, + llmod: llvm::ModuleRef, + tm: llvm::TargetMachineRef, +} + +unsafe impl Send for ModuleLlvm { } +unsafe impl Sync for ModuleLlvm { } + +impl Drop for ModuleLlvm { + fn drop(&mut self) { + unsafe { + llvm::LLVMDisposeModule(self.llmod); + llvm::LLVMContextDispose(self.llcx); + llvm::LLVMRustDisposeTargetMachine(self.tm); + } + } +} + +struct CodegenResults { + crate_name: Symbol, + modules: Vec, + allocator_module: Option, + metadata_module: CompiledModule, + link: rustc::middle::cstore::LinkMeta, + metadata: rustc::middle::cstore::EncodedMetadata, + windows_subsystem: Option, + linker_info: back::linker::LinkerInfo, + crate_info: CrateInfo, +} + +// Misc info we load from metadata to persist beyond the tcx +struct CrateInfo { + panic_runtime: Option, + compiler_builtins: Option, + profiler_runtime: Option, + sanitizer_runtime: Option, + is_no_builtins: FxHashSet, + native_libraries: FxHashMap>>, + crate_name: FxHashMap, + used_libraries: Lrc>, + link_args: Lrc>, + used_crate_source: FxHashMap>, + used_crates_static: Vec<(CrateNum, LibSource)>, + used_crates_dynamic: Vec<(CrateNum, LibSource)>, + wasm_custom_sections: BTreeMap>, + wasm_imports: FxHashMap, + lang_item_to_crate: FxHashMap, + missing_lang_items: FxHashMap>, +} + +__build_diagnostic_array! { librustc_codegen_llvm, DIAGNOSTICS } diff --git a/src/librustc_codegen_llvm/llvm_util.rs b/src/librustc_codegen_llvm/llvm_util.rs new file mode 100644 index 00000000000..357b639e788 --- /dev/null +++ b/src/librustc_codegen_llvm/llvm_util.rs @@ -0,0 +1,255 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use syntax_pos::symbol::Symbol; +use back::write::create_target_machine; +use llvm; +use rustc::session::Session; +use rustc::session::config::PrintRequest; +use libc::c_int; +use std::ffi::CString; +use syntax::feature_gate::UnstableFeatures; + +use std::sync::atomic::{AtomicBool, Ordering}; +use std::sync::Once; + +static POISONED: AtomicBool = AtomicBool::new(false); +static INIT: Once = Once::new(); + +pub(crate) fn init(sess: &Session) { + unsafe { + // Before we touch LLVM, make sure that multithreading is enabled. + INIT.call_once(|| { + if llvm::LLVMStartMultithreaded() != 1 { + // use an extra bool to make sure that all future usage of LLVM + // cannot proceed despite the Once not running more than once. + POISONED.store(true, Ordering::SeqCst); + } + + configure_llvm(sess); + }); + + if POISONED.load(Ordering::SeqCst) { + bug!("couldn't enable multi-threaded LLVM"); + } + } +} + +fn require_inited() { + INIT.call_once(|| bug!("llvm is not initialized")); + if POISONED.load(Ordering::SeqCst) { + bug!("couldn't enable multi-threaded LLVM"); + } +} + +unsafe fn configure_llvm(sess: &Session) { + let mut llvm_c_strs = Vec::new(); + let mut llvm_args = Vec::new(); + + { + let mut add = |arg: &str| { + let s = CString::new(arg).unwrap(); + llvm_args.push(s.as_ptr()); + llvm_c_strs.push(s); + }; + add("rustc"); // fake program name + if sess.time_llvm_passes() { add("-time-passes"); } + if sess.print_llvm_passes() { add("-debug-pass=Structure"); } + if sess.opts.debugging_opts.disable_instrumentation_preinliner { + add("-disable-preinline"); + } + + for arg in &sess.opts.cg.llvm_args { + add(&(*arg)); + } + } + + llvm::LLVMInitializePasses(); + + llvm::initialize_available_targets(); + + llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, + llvm_args.as_ptr()); +} + +// WARNING: the features after applying `to_llvm_feature` must be known +// to LLVM or the feature detection code will walk past the end of the feature +// array, leading to crashes. + +const ARM_WHITELIST: &[(&str, Option<&str>)] = &[ + ("neon", Some("arm_target_feature")), + ("v7", Some("arm_target_feature")), + ("vfp2", Some("arm_target_feature")), + ("vfp3", Some("arm_target_feature")), + ("vfp4", Some("arm_target_feature")), +]; + +const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[ + ("fp", Some("aarch64_target_feature")), + ("neon", Some("aarch64_target_feature")), + ("sve", Some("aarch64_target_feature")), + ("crc", Some("aarch64_target_feature")), + ("crypto", Some("aarch64_target_feature")), + ("ras", Some("aarch64_target_feature")), + ("lse", Some("aarch64_target_feature")), + ("rdm", Some("aarch64_target_feature")), + ("fp16", Some("aarch64_target_feature")), + ("rcpc", Some("aarch64_target_feature")), + ("dotprod", Some("aarch64_target_feature")), + ("v8.1a", Some("aarch64_target_feature")), + ("v8.2a", Some("aarch64_target_feature")), + ("v8.3a", Some("aarch64_target_feature")), +]; + +const X86_WHITELIST: &[(&str, Option<&str>)] = &[ + ("aes", None), + ("avx", None), + ("avx2", None), + ("avx512bw", Some("avx512_target_feature")), + ("avx512cd", Some("avx512_target_feature")), + ("avx512dq", Some("avx512_target_feature")), + ("avx512er", Some("avx512_target_feature")), + ("avx512f", Some("avx512_target_feature")), + ("avx512ifma", Some("avx512_target_feature")), + ("avx512pf", Some("avx512_target_feature")), + ("avx512vbmi", Some("avx512_target_feature")), + ("avx512vl", Some("avx512_target_feature")), + ("avx512vpopcntdq", Some("avx512_target_feature")), + ("bmi1", None), + ("bmi2", None), + ("fma", None), + ("fxsr", None), + ("lzcnt", None), + ("mmx", Some("mmx_target_feature")), + ("pclmulqdq", None), + ("popcnt", None), + ("rdrand", None), + ("rdseed", None), + ("sha", None), + ("sse", None), + ("sse2", None), + ("sse3", None), + ("sse4.1", None), + ("sse4.2", None), + ("sse4a", Some("sse4a_target_feature")), + ("ssse3", None), + ("tbm", Some("tbm_target_feature")), + ("xsave", None), + ("xsavec", None), + ("xsaveopt", None), + ("xsaves", None), +]; + +const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[ + ("hvx", Some("hexagon_target_feature")), + ("hvx-double", Some("hexagon_target_feature")), +]; + +const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[ + ("altivec", Some("powerpc_target_feature")), + ("power8-altivec", Some("powerpc_target_feature")), + ("power9-altivec", Some("powerpc_target_feature")), + ("power8-vector", Some("powerpc_target_feature")), + ("power9-vector", Some("powerpc_target_feature")), + ("vsx", Some("powerpc_target_feature")), +]; + +const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[ + ("fp64", Some("mips_target_feature")), + ("msa", Some("mips_target_feature")), +]; + +/// When rustdoc is running, provide a list of all known features so that all their respective +/// primtives may be documented. +/// +/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this +/// iterator! +pub fn all_known_features() -> impl Iterator)> { + ARM_WHITELIST.iter().cloned() + .chain(AARCH64_WHITELIST.iter().cloned()) + .chain(X86_WHITELIST.iter().cloned()) + .chain(HEXAGON_WHITELIST.iter().cloned()) + .chain(POWERPC_WHITELIST.iter().cloned()) + .chain(MIPS_WHITELIST.iter().cloned()) +} + +pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { + let arch = if sess.target.target.arch == "x86_64" { + "x86" + } else { + &*sess.target.target.arch + }; + match (arch, s) { + ("x86", "pclmulqdq") => "pclmul", + ("x86", "rdrand") => "rdrnd", + ("x86", "bmi1") => "bmi", + ("aarch64", "fp") => "fp-armv8", + ("aarch64", "fp16") => "fullfp16", + (_, s) => s, + } +} + +pub fn target_features(sess: &Session) -> Vec { + let target_machine = create_target_machine(sess, true); + target_feature_whitelist(sess) + .iter() + .filter_map(|&(feature, gate)| { + if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() { + Some(feature) + } else { + None + } + }) + .filter(|feature| { + let llvm_feature = to_llvm_feature(sess, feature); + let cstr = CString::new(llvm_feature).unwrap(); + unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } + }) + .map(|feature| Symbol::intern(feature)).collect() +} + +pub fn target_feature_whitelist(sess: &Session) + -> &'static [(&'static str, Option<&'static str>)] +{ + match &*sess.target.target.arch { + "arm" => ARM_WHITELIST, + "aarch64" => AARCH64_WHITELIST, + "x86" | "x86_64" => X86_WHITELIST, + "hexagon" => HEXAGON_WHITELIST, + "mips" | "mips64" => MIPS_WHITELIST, + "powerpc" | "powerpc64" => POWERPC_WHITELIST, + _ => &[], + } +} + +pub fn print_version() { + // Can be called without initializing LLVM + unsafe { + println!("LLVM version: {}.{}", + llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor()); + } +} + +pub fn print_passes() { + // Can be called without initializing LLVM + unsafe { llvm::LLVMRustPrintPasses(); } +} + +pub(crate) fn print(req: PrintRequest, sess: &Session) { + require_inited(); + let tm = create_target_machine(sess, true); + unsafe { + match req { + PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), + PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), + _ => bug!("rustc_codegen_llvm can't handle print request: {:?}", req), + } + } +} diff --git a/src/librustc_codegen_llvm/metadata.rs b/src/librustc_codegen_llvm/metadata.rs new file mode 100644 index 00000000000..144baa65c1b --- /dev/null +++ b/src/librustc_codegen_llvm/metadata.rs @@ -0,0 +1,124 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::util::common; +use rustc::middle::cstore::MetadataLoader; +use rustc_target::spec::Target; +use llvm; +use llvm::{False, ObjectFile, mk_section_iter}; +use llvm::archive_ro::ArchiveRO; + +use rustc_data_structures::owning_ref::OwningRef; +use std::path::Path; +use std::ptr; +use std::slice; + +pub use rustc_data_structures::sync::MetadataRef; + +pub const METADATA_FILENAME: &str = "rust.metadata.bin"; + +pub struct LlvmMetadataLoader; + +impl MetadataLoader for LlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { + // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap + // internally to read the file. We also avoid even using a memcpy by + // just keeping the archive along while the metadata is in use. + let archive = ArchiveRO::open(filename) + .map(|ar| OwningRef::new(box ar)) + .map_err(|e| { + debug!("llvm didn't like `{}`: {}", filename.display(), e); + format!("failed to read rlib metadata in '{}': {}", filename.display(), e) + })?; + let buf: OwningRef<_, [u8]> = archive + .try_map(|ar| { + ar.iter() + .filter_map(|s| s.ok()) + .find(|sect| sect.name() == Some(METADATA_FILENAME)) + .map(|s| s.data()) + .ok_or_else(|| { + debug!("didn't find '{}' in the archive", METADATA_FILENAME); + format!("failed to read rlib metadata: '{}'", + filename.display()) + }) + })?; + Ok(rustc_erase_owner!(buf)) + } + + fn get_dylib_metadata(&self, + target: &Target, + filename: &Path) + -> Result { + unsafe { + let buf = common::path2cstr(filename); + let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); + if mb as isize == 0 { + return Err(format!("error reading library: '{}'", filename.display())); + } + let of = ObjectFile::new(mb) + .map(|of| OwningRef::new(box of)) + .ok_or_else(|| format!("provided path not an object file: '{}'", + filename.display()))?; + let buf = of.try_map(|of| search_meta_section(of, target, filename))?; + Ok(rustc_erase_owner!(buf)) + } + } +} + +fn search_meta_section<'a>(of: &'a ObjectFile, + target: &Target, + filename: &Path) + -> Result<&'a [u8], String> { + unsafe { + let si = mk_section_iter(of.llof); + while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { + let mut name_buf = ptr::null(); + let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); + let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec(); + let name = String::from_utf8(name).unwrap(); + debug!("get_metadata_section: name {}", name); + if read_metadata_section_name(target) == name { + let cbuf = llvm::LLVMGetSectionContents(si.llsi); + let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; + // The buffer is valid while the object file is around + let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz); + return Ok(buf); + } + llvm::LLVMMoveToNextSection(si.llsi); + } + } + Err(format!("metadata not found: '{}'", filename.display())) +} + +pub fn metadata_section_name(target: &Target) -> &'static str { + // Historical note: + // + // When using link.exe it was seen that the section name `.note.rustc` + // was getting shortened to `.note.ru`, and according to the PE and COFF + // specification: + // + // > Executable images do not use a string table and do not support + // > section names longer than 8 characters + // + // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx + // + // As a result, we choose a slightly shorter name! As to why + // `.note.rustc` works on MinGW, that's another good question... + + if target.options.is_like_osx { + "__DATA,.rustc" + } else { + ".rustc" + } +} + +fn read_metadata_section_name(_target: &Target) -> &'static str { + ".rustc" +} diff --git a/src/librustc_codegen_llvm/meth.rs b/src/librustc_codegen_llvm/meth.rs new file mode 100644 index 00000000000..21bbdf31dcb --- /dev/null +++ b/src/librustc_codegen_llvm/meth.rs @@ -0,0 +1,115 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use abi::{FnType, FnTypeExt}; +use callee; +use common::*; +use builder::Builder; +use consts; +use monomorphize; +use type_::Type; +use value::Value; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::HasDataLayout; +use debuginfo; + +#[derive(Copy, Clone, Debug)] +pub struct VirtualIndex(u64); + +pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); +pub const SIZE: VirtualIndex = VirtualIndex(1); +pub const ALIGN: VirtualIndex = VirtualIndex(2); + +impl<'a, 'tcx> VirtualIndex { + pub fn from_index(index: usize) -> Self { + VirtualIndex(index as u64 + 3) + } + + pub fn get_fn(self, bx: &Builder<'a, 'tcx>, + llvtable: ValueRef, + fn_ty: &FnType<'tcx, Ty<'tcx>>) -> ValueRef { + // Load the data pointer from the object. + debug!("get_fn({:?}, {:?})", Value(llvtable), self); + + let llvtable = bx.pointercast(llvtable, fn_ty.llvm_type(bx.cx).ptr_to().ptr_to()); + let ptr_align = bx.tcx().data_layout.pointer_align; + let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), ptr_align); + bx.nonnull_metadata(ptr); + // Vtable loads are invariant + bx.set_invariant_load(ptr); + ptr + } + + pub fn get_usize(self, bx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { + // Load the data pointer from the object. + debug!("get_int({:?}, {:?})", Value(llvtable), self); + + let llvtable = bx.pointercast(llvtable, Type::isize(bx.cx).ptr_to()); + let usize_align = bx.tcx().data_layout.pointer_align; + let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), usize_align); + // Vtable loads are invariant + bx.set_invariant_load(ptr); + ptr + } +} + +/// Creates a dynamic vtable for the given type and vtable origin. +/// This is used only for objects. +/// +/// The vtables are cached instead of created on every call. +/// +/// The `trait_ref` encodes the erased self type. Hence if we are +/// making an object `Foo` from a value of type `Foo`, then +/// `trait_ref` would map `T:Trait`. +pub fn get_vtable<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + ty: Ty<'tcx>, + trait_ref: Option>) + -> ValueRef +{ + let tcx = cx.tcx; + + debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); + + // Check the cache. + if let Some(&val) = cx.vtables.borrow().get(&(ty, trait_ref)) { + return val; + } + + // Not in the cache. Build it. + let nullptr = C_null(Type::i8p(cx)); + + let (size, align) = cx.size_and_align_of(ty); + let mut components: Vec<_> = [ + callee::get_fn(cx, monomorphize::resolve_drop_in_place(cx.tcx, ty)), + C_usize(cx, size.bytes()), + C_usize(cx, align.abi()) + ].iter().cloned().collect(); + + if let Some(trait_ref) = trait_ref { + let trait_ref = trait_ref.with_self_ty(tcx, ty); + let methods = tcx.vtable_methods(trait_ref); + let methods = methods.iter().cloned().map(|opt_mth| { + opt_mth.map_or(nullptr, |(def_id, substs)| { + callee::resolve_and_get_fn(cx, def_id, substs) + }) + }); + components.extend(methods); + } + + let vtable_const = C_struct(cx, &components, false); + let align = cx.data_layout().pointer_align; + let vtable = consts::addr_of(cx, vtable_const, align, "vtable"); + + debuginfo::create_vtable_metadata(cx, ty, vtable); + + cx.vtables.borrow_mut().insert((ty, trait_ref), vtable); + vtable +} diff --git a/src/librustc_codegen_llvm/mir/analyze.rs b/src/librustc_codegen_llvm/mir/analyze.rs new file mode 100644 index 00000000000..9e5298eb736 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/analyze.rs @@ -0,0 +1,361 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! An analysis to determine which locals require allocas and +//! which do not. + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::control_flow_graph::dominators::Dominators; +use rustc_data_structures::indexed_vec::{Idx, IndexVec}; +use rustc::mir::{self, Location, TerminatorKind}; +use rustc::mir::visit::{Visitor, PlaceContext}; +use rustc::mir::traversal; +use rustc::ty; +use rustc::ty::layout::LayoutOf; +use type_of::LayoutLlvmExt; +use super::FunctionCx; + +pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { + let mir = fx.mir; + let mut analyzer = LocalAnalyzer::new(fx); + + analyzer.visit_mir(mir); + + for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { + let ty = fx.monomorphize(&ty); + debug!("local {} has type {:?}", index, ty); + let layout = fx.cx.layout_of(ty); + if layout.is_llvm_immediate() { + // These sorts of types are immediates that we can store + // in an ValueRef without an alloca. + } else if layout.is_llvm_scalar_pair() { + // We allow pairs and uses of any of their 2 fields. + } else { + // These sorts of types require an alloca. Note that + // is_llvm_immediate() may *still* be true, particularly + // for newtypes, but we currently force some types + // (e.g. structs) into an alloca unconditionally, just so + // that we don't have to deal with having two pathways + // (gep vs extractvalue etc). + analyzer.not_ssa(mir::Local::new(index)); + } + } + + analyzer.non_ssa_locals +} + +struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { + fx: &'mir FunctionCx<'a, 'tcx>, + dominators: Dominators, + non_ssa_locals: BitVector, + // The location of the first visited direct assignment to each + // local, or an invalid location (out of bounds `block` index). + first_assignment: IndexVec +} + +impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { + fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { + let invalid_location = + mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); + let mut analyzer = LocalAnalyzer { + fx, + dominators: fx.mir.dominators(), + non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), + first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) + }; + + // Arguments get assigned to by means of the function being called + for arg in fx.mir.args_iter() { + analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); + } + + analyzer + } + + fn first_assignment(&self, local: mir::Local) -> Option { + let location = self.first_assignment[local]; + if location.block.index() < self.fx.mir.basic_blocks().len() { + Some(location) + } else { + None + } + } + + fn not_ssa(&mut self, local: mir::Local) { + debug!("marking {:?} as non-SSA", local); + self.non_ssa_locals.insert(local.index()); + } + + fn assign(&mut self, local: mir::Local, location: Location) { + if self.first_assignment(local).is_some() { + self.not_ssa(local); + } else { + self.first_assignment[local] = location; + } + } +} + +impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { + fn visit_assign(&mut self, + block: mir::BasicBlock, + place: &mir::Place<'tcx>, + rvalue: &mir::Rvalue<'tcx>, + location: Location) { + debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); + + if let mir::Place::Local(index) = *place { + self.assign(index, location); + if !self.fx.rvalue_creates_operand(rvalue) { + self.not_ssa(index); + } + } else { + self.visit_place(place, PlaceContext::Store, location); + } + + self.visit_rvalue(rvalue, location); + } + + fn visit_terminator_kind(&mut self, + block: mir::BasicBlock, + kind: &mir::TerminatorKind<'tcx>, + location: Location) { + let check = match *kind { + mir::TerminatorKind::Call { + func: mir::Operand::Constant(ref c), + ref args, .. + } => match c.ty.sty { + ty::TyFnDef(did, _) => Some((did, args)), + _ => None, + }, + _ => None, + }; + if let Some((def_id, args)) = check { + if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { + // box_free(x) shares with `drop x` the property that it + // is not guaranteed to be statically dominated by the + // definition of x, so x must always be in an alloca. + if let mir::Operand::Move(ref place) = args[0] { + self.visit_place(place, PlaceContext::Drop, location); + } + } + } + + self.super_terminator_kind(block, kind, location); + } + + fn visit_place(&mut self, + place: &mir::Place<'tcx>, + context: PlaceContext<'tcx>, + location: Location) { + debug!("visit_place(place={:?}, context={:?})", place, context); + let cx = self.fx.cx; + + if let mir::Place::Projection(ref proj) = *place { + // Allow uses of projections that are ZSTs or from scalar fields. + let is_consume = match context { + PlaceContext::Copy | PlaceContext::Move => true, + _ => false + }; + if is_consume { + let base_ty = proj.base.ty(self.fx.mir, cx.tcx); + let base_ty = self.fx.monomorphize(&base_ty); + + // ZSTs don't require any actual memory access. + let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); + let elem_ty = self.fx.monomorphize(&elem_ty); + if cx.layout_of(elem_ty).is_zst() { + return; + } + + if let mir::ProjectionElem::Field(..) = proj.elem { + let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); + if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { + // Recurse with the same context, instead of `Projection`, + // potentially stopping at non-operand projections, + // which would trigger `not_ssa` on locals. + self.visit_place(&proj.base, context, location); + return; + } + } + } + + // A deref projection only reads the pointer, never needs the place. + if let mir::ProjectionElem::Deref = proj.elem { + return self.visit_place(&proj.base, PlaceContext::Copy, location); + } + } + + self.super_place(place, context, location); + } + + fn visit_local(&mut self, + &local: &mir::Local, + context: PlaceContext<'tcx>, + location: Location) { + match context { + PlaceContext::Call => { + self.assign(local, location); + } + + PlaceContext::StorageLive | + PlaceContext::StorageDead | + PlaceContext::Validate => {} + + PlaceContext::Copy | + PlaceContext::Move => { + // Reads from uninitialized variables (e.g. in dead code, after + // optimizations) require locals to be in (uninitialized) memory. + // NB: there can be uninitialized reads of a local visited after + // an assignment to that local, if they happen on disjoint paths. + let ssa_read = match self.first_assignment(local) { + Some(assignment_location) => { + assignment_location.dominates(location, &self.dominators) + } + None => false + }; + if !ssa_read { + self.not_ssa(local); + } + } + + PlaceContext::Inspect | + PlaceContext::Store | + PlaceContext::AsmOutput | + PlaceContext::Borrow { .. } | + PlaceContext::Projection(..) => { + self.not_ssa(local); + } + + PlaceContext::Drop => { + let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); + let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); + + // Only need the place if we're actually dropping it. + if self.fx.cx.type_needs_drop(ty) { + self.not_ssa(local); + } + } + } + } +} + +#[derive(Copy, Clone, Debug, PartialEq, Eq)] +pub enum CleanupKind { + NotCleanup, + Funclet, + Internal { funclet: mir::BasicBlock } +} + +impl CleanupKind { + pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option { + match self { + CleanupKind::NotCleanup => None, + CleanupKind::Funclet => Some(for_bb), + CleanupKind::Internal { funclet } => Some(funclet), + } + } +} + +pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { + fn discover_masters<'tcx>(result: &mut IndexVec, + mir: &mir::Mir<'tcx>) { + for (bb, data) in mir.basic_blocks().iter_enumerated() { + match data.terminator().kind { + TerminatorKind::Goto { .. } | + TerminatorKind::Resume | + TerminatorKind::Abort | + TerminatorKind::Return | + TerminatorKind::GeneratorDrop | + TerminatorKind::Unreachable | + TerminatorKind::SwitchInt { .. } | + TerminatorKind::Yield { .. } | + TerminatorKind::FalseEdges { .. } | + TerminatorKind::FalseUnwind { .. } => { + /* nothing to do */ + } + TerminatorKind::Call { cleanup: unwind, .. } | + TerminatorKind::Assert { cleanup: unwind, .. } | + TerminatorKind::DropAndReplace { unwind, .. } | + TerminatorKind::Drop { unwind, .. } => { + if let Some(unwind) = unwind { + debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet", + bb, data, unwind); + result[unwind] = CleanupKind::Funclet; + } + } + } + } + } + + fn propagate<'tcx>(result: &mut IndexVec, + mir: &mir::Mir<'tcx>) { + let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks()); + + let mut set_successor = |funclet: mir::BasicBlock, succ| { + match funclet_succs[funclet] { + ref mut s @ None => { + debug!("set_successor: updating successor of {:?} to {:?}", + funclet, succ); + *s = Some(succ); + }, + Some(s) => if s != succ { + span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}", + funclet, s, succ); + } + } + }; + + for (bb, data) in traversal::reverse_postorder(mir) { + let funclet = match result[bb] { + CleanupKind::NotCleanup => continue, + CleanupKind::Funclet => bb, + CleanupKind::Internal { funclet } => funclet, + }; + + debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}", + bb, data, result[bb], funclet); + + for &succ in data.terminator().successors() { + let kind = result[succ]; + debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", + funclet, succ, kind); + match kind { + CleanupKind::NotCleanup => { + result[succ] = CleanupKind::Internal { funclet: funclet }; + } + CleanupKind::Funclet => { + if funclet != succ { + set_successor(funclet, succ); + } + } + CleanupKind::Internal { funclet: succ_funclet } => { + if funclet != succ_funclet { + // `succ` has 2 different funclet going into it, so it must + // be a funclet by itself. + + debug!("promoting {:?} to a funclet and updating {:?}", succ, + succ_funclet); + result[succ] = CleanupKind::Funclet; + set_successor(succ_funclet, succ); + set_successor(funclet, succ); + } + } + } + } + } + } + + let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks()); + + discover_masters(&mut result, mir); + propagate(&mut result, mir); + debug!("cleanup_kinds: result={:?}", result); + result +} diff --git a/src/librustc_codegen_llvm/mir/block.rs b/src/librustc_codegen_llvm/mir/block.rs new file mode 100644 index 00000000000..556baeba39e --- /dev/null +++ b/src/librustc_codegen_llvm/mir/block.rs @@ -0,0 +1,895 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef, BasicBlockRef}; +use rustc::middle::lang_items; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::mir; +use rustc::mir::interpret::EvalErrorKind; +use abi::{Abi, ArgType, ArgTypeExt, FnType, FnTypeExt, LlvmType, PassMode}; +use base; +use callee; +use builder::{Builder, MemFlags}; +use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef}; +use consts; +use meth; +use monomorphize; +use type_of::LayoutLlvmExt; +use type_::Type; + +use syntax::symbol::Symbol; +use syntax_pos::Pos; + +use super::{FunctionCx, LocalRef}; +use super::place::PlaceRef; +use super::operand::OperandRef; +use super::operand::OperandValue::{Pair, Ref, Immediate}; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_block(&mut self, bb: mir::BasicBlock) { + let mut bx = self.build_block(bb); + let data = &self.mir[bb]; + + debug!("codegen_block({:?}={:?})", bb, data); + + for statement in &data.statements { + bx = self.codegen_statement(bx, statement); + } + + self.codegen_terminator(bx, bb, data.terminator()); + } + + fn codegen_terminator(&mut self, + mut bx: Builder<'a, 'tcx>, + bb: mir::BasicBlock, + terminator: &mir::Terminator<'tcx>) + { + debug!("codegen_terminator: {:?}", terminator); + + // Create the cleanup bundle, if needed. + let tcx = bx.tcx(); + let span = terminator.source_info.span; + let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); + let funclet = funclet_bb.and_then(|funclet_bb| self.funclets[funclet_bb].as_ref()); + + let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); + let cleanup_bundle = funclet.map(|l| l.bundle()); + + let lltarget = |this: &mut Self, target: mir::BasicBlock| { + let lltarget = this.blocks[target]; + let target_funclet = this.cleanup_kinds[target].funclet_bb(target); + match (funclet_bb, target_funclet) { + (None, None) => (lltarget, false), + (Some(f), Some(t_f)) + if f == t_f || !base::wants_msvc_seh(tcx.sess) + => (lltarget, false), + (None, Some(_)) => { + // jump *into* cleanup - need a landing pad if GNU + (this.landing_pad_to(target), false) + } + (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), + (Some(_), Some(_)) => { + (this.landing_pad_to(target), true) + } + } + }; + + let llblock = |this: &mut Self, target: mir::BasicBlock| { + let (lltarget, is_cleanupret) = lltarget(this, target); + if is_cleanupret { + // MSVC cross-funclet jump - need a trampoline + + debug!("llblock: creating cleanup trampoline for {:?}", target); + let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); + let trampoline = this.new_block(name); + trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); + trampoline.llbb() + } else { + lltarget + } + }; + + let funclet_br = |this: &mut Self, bx: Builder, target: mir::BasicBlock| { + let (lltarget, is_cleanupret) = lltarget(this, target); + if is_cleanupret { + // micro-optimization: generate a `ret` rather than a jump + // to a trampoline. + bx.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); + } else { + bx.br(lltarget); + } + }; + + let do_call = | + this: &mut Self, + bx: Builder<'a, 'tcx>, + fn_ty: FnType<'tcx, Ty<'tcx>>, + fn_ptr: ValueRef, + llargs: &[ValueRef], + destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>, + cleanup: Option + | { + if let Some(cleanup) = cleanup { + let ret_bx = if let Some((_, target)) = destination { + this.blocks[target] + } else { + this.unreachable_block() + }; + let invokeret = bx.invoke(fn_ptr, + &llargs, + ret_bx, + llblock(this, cleanup), + cleanup_bundle); + fn_ty.apply_attrs_callsite(&bx, invokeret); + + if let Some((ret_dest, target)) = destination { + let ret_bx = this.build_block(target); + this.set_debug_loc(&ret_bx, terminator.source_info); + this.store_return(&ret_bx, ret_dest, &fn_ty.ret, invokeret); + } + } else { + let llret = bx.call(fn_ptr, &llargs, cleanup_bundle); + fn_ty.apply_attrs_callsite(&bx, llret); + if this.mir[bb].is_cleanup { + // Cleanup is always the cold path. Don't inline + // drop glue. Also, when there is a deeply-nested + // struct, there are "symmetry" issues that cause + // exponential inlining - see issue #41696. + llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); + } + + if let Some((ret_dest, target)) = destination { + this.store_return(&bx, ret_dest, &fn_ty.ret, llret); + funclet_br(this, bx, target); + } else { + bx.unreachable(); + } + } + }; + + self.set_debug_loc(&bx, terminator.source_info); + match terminator.kind { + mir::TerminatorKind::Resume => { + if let Some(cleanup_pad) = cleanup_pad { + bx.cleanup_ret(cleanup_pad, None); + } else { + let slot = self.get_personality_slot(&bx); + let lp0 = slot.project_field(&bx, 0).load(&bx).immediate(); + let lp1 = slot.project_field(&bx, 1).load(&bx).immediate(); + slot.storage_dead(&bx); + + if !bx.sess().target.target.options.custom_unwind_resume { + let mut lp = C_undef(self.landing_pad_type()); + lp = bx.insert_value(lp, lp0, 0); + lp = bx.insert_value(lp, lp1, 1); + bx.resume(lp); + } else { + bx.call(bx.cx.eh_unwind_resume(), &[lp0], cleanup_bundle); + bx.unreachable(); + } + } + } + + mir::TerminatorKind::Abort => { + // Call core::intrinsics::abort() + let fnname = bx.cx.get_intrinsic(&("llvm.trap")); + bx.call(fnname, &[], None); + bx.unreachable(); + } + + mir::TerminatorKind::Goto { target } => { + funclet_br(self, bx, target); + } + + mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { + let discr = self.codegen_operand(&bx, discr); + if switch_ty == bx.tcx().types.bool { + let lltrue = llblock(self, targets[0]); + let llfalse = llblock(self, targets[1]); + if let [0] = values[..] { + bx.cond_br(discr.immediate(), llfalse, lltrue); + } else { + assert_eq!(&values[..], &[1]); + bx.cond_br(discr.immediate(), lltrue, llfalse); + } + } else { + let (otherwise, targets) = targets.split_last().unwrap(); + let switch = bx.switch(discr.immediate(), + llblock(self, *otherwise), values.len()); + let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); + for (&value, target) in values.iter().zip(targets) { + let llval = C_uint_big(switch_llty, value); + let llbb = llblock(self, *target); + bx.add_case(switch, llval, llbb) + } + } + } + + mir::TerminatorKind::Return => { + let llval = match self.fn_ty.ret.mode { + PassMode::Ignore | PassMode::Indirect(_) => { + bx.ret_void(); + return; + } + + PassMode::Direct(_) | PassMode::Pair(..) => { + let op = self.codegen_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE)); + if let Ref(llval, align) = op.val { + bx.load(llval, align) + } else { + op.immediate_or_packed_pair(&bx) + } + } + + PassMode::Cast(cast_ty) => { + let op = match self.locals[mir::RETURN_PLACE] { + LocalRef::Operand(Some(op)) => op, + LocalRef::Operand(None) => bug!("use of return before def"), + LocalRef::Place(cg_place) => { + OperandRef { + val: Ref(cg_place.llval, cg_place.align), + layout: cg_place.layout + } + } + }; + let llslot = match op.val { + Immediate(_) | Pair(..) => { + let scratch = PlaceRef::alloca(&bx, self.fn_ty.ret.layout, "ret"); + op.val.store(&bx, scratch); + scratch.llval + } + Ref(llval, align) => { + assert_eq!(align.abi(), op.layout.align.abi(), + "return place is unaligned!"); + llval + } + }; + bx.load( + bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()), + self.fn_ty.ret.layout.align) + } + }; + bx.ret(llval); + } + + mir::TerminatorKind::Unreachable => { + bx.unreachable(); + } + + mir::TerminatorKind::Drop { ref location, target, unwind } => { + let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); + let ty = self.monomorphize(&ty); + let drop_fn = monomorphize::resolve_drop_in_place(bx.cx.tcx, ty); + + if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { + // we don't actually need to drop anything. + funclet_br(self, bx, target); + return + } + + let place = self.codegen_place(&bx, location); + let mut args: &[_] = &[place.llval, place.llextra]; + args = &args[..1 + place.has_extra() as usize]; + let (drop_fn, fn_ty) = match ty.sty { + ty::TyDynamic(..) => { + let fn_ty = drop_fn.ty(bx.cx.tcx); + let sig = common::ty_fn_sig(bx.cx, fn_ty); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let fn_ty = FnType::new_vtable(bx.cx, sig, &[]); + args = &args[..1]; + (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty) + } + _ => { + (callee::get_fn(bx.cx, drop_fn), + FnType::of_instance(bx.cx, &drop_fn)) + } + }; + do_call(self, bx, fn_ty, drop_fn, args, + Some((ReturnDest::Nothing, target)), + unwind); + } + + mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { + let cond = self.codegen_operand(&bx, cond).immediate(); + let mut const_cond = common::const_to_opt_u128(cond, false).map(|c| c == 1); + + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + // NOTE: Unlike binops, negation doesn't have its own + // checked operation, just a comparison with the minimum + // value, so we have to check for the assert message. + if !bx.cx.check_overflow { + if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { + const_cond = Some(expected); + } + } + + // Don't codegen the panic block if success if known. + if const_cond == Some(expected) { + funclet_br(self, bx, target); + return; + } + + // Pass the condition through llvm.expect for branch hinting. + let expect = bx.cx.get_intrinsic(&"llvm.expect.i1"); + let cond = bx.call(expect, &[cond, C_bool(bx.cx, expected)], None); + + // Create the failure block and the conditional branch to it. + let lltarget = llblock(self, target); + let panic_block = self.new_block("panic"); + if expected { + bx.cond_br(cond, lltarget, panic_block.llbb()); + } else { + bx.cond_br(cond, panic_block.llbb(), lltarget); + } + + // After this point, bx is the block for the call to panic. + bx = panic_block; + self.set_debug_loc(&bx, terminator.source_info); + + // Get the location information. + let loc = bx.sess().codemap().lookup_char_pos(span.lo()); + let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); + let filename = C_str_slice(bx.cx, filename); + let line = C_u32(bx.cx, loc.line as u32); + let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1); + let align = tcx.data_layout.aggregate_align + .max(tcx.data_layout.i32_align) + .max(tcx.data_layout.pointer_align); + + // Put together the arguments to the panic entry point. + let (lang_item, args) = match *msg { + EvalErrorKind::BoundsCheck { ref len, ref index } => { + let len = self.codegen_operand(&mut bx, len).immediate(); + let index = self.codegen_operand(&mut bx, index).immediate(); + + let file_line_col = C_struct(bx.cx, &[filename, line, col], false); + let file_line_col = consts::addr_of(bx.cx, + file_line_col, + align, + "panic_bounds_check_loc"); + (lang_items::PanicBoundsCheckFnLangItem, + vec![file_line_col, index, len]) + } + _ => { + let str = msg.description(); + let msg_str = Symbol::intern(str).as_str(); + let msg_str = C_str_slice(bx.cx, msg_str); + let msg_file_line_col = C_struct(bx.cx, + &[msg_str, filename, line, col], + false); + let msg_file_line_col = consts::addr_of(bx.cx, + msg_file_line_col, + align, + "panic_loc"); + (lang_items::PanicFnLangItem, + vec![msg_file_line_col]) + } + }; + + // Obtain the panic entry point. + let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); + let instance = ty::Instance::mono(bx.tcx(), def_id); + let fn_ty = FnType::of_instance(bx.cx, &instance); + let llfn = callee::get_fn(bx.cx, instance); + + // Codegen the actual panic invoke/call. + do_call(self, bx, fn_ty, llfn, &args, None, cleanup); + } + + mir::TerminatorKind::DropAndReplace { .. } => { + bug!("undesugared DropAndReplace in codegen: {:?}", terminator); + } + + mir::TerminatorKind::Call { ref func, ref args, ref destination, cleanup } => { + // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. + let callee = self.codegen_operand(&bx, func); + + let (instance, mut llfn) = match callee.layout.ty.sty { + ty::TyFnDef(def_id, substs) => { + (Some(ty::Instance::resolve(bx.cx.tcx, + ty::ParamEnv::reveal_all(), + def_id, + substs).unwrap()), + None) + } + ty::TyFnPtr(_) => { + (None, Some(callee.immediate())) + } + _ => bug!("{} is not callable", callee.layout.ty) + }; + let def = instance.map(|i| i.def); + let sig = callee.layout.ty.fn_sig(bx.tcx()); + let sig = bx.tcx().normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + let abi = sig.abi; + + // Handle intrinsics old codegen wants Expr's for, ourselves. + let intrinsic = match def { + Some(ty::InstanceDef::Intrinsic(def_id)) + => Some(bx.tcx().item_name(def_id).as_str()), + _ => None + }; + let intrinsic = intrinsic.as_ref().map(|s| &s[..]); + + if intrinsic == Some("transmute") { + let &(ref dest, target) = destination.as_ref().unwrap(); + self.codegen_transmute(&bx, &args[0], dest); + funclet_br(self, bx, target); + return; + } + + let extra_args = &args[sig.inputs().len()..]; + let extra_args = extra_args.iter().map(|op_arg| { + let op_ty = op_arg.ty(self.mir, bx.tcx()); + self.monomorphize(&op_ty) + }).collect::>(); + + let fn_ty = match def { + Some(ty::InstanceDef::Virtual(..)) => { + FnType::new_vtable(bx.cx, sig, &extra_args) + } + Some(ty::InstanceDef::DropGlue(_, None)) => { + // empty drop glue - a nop. + let &(_, target) = destination.as_ref().unwrap(); + funclet_br(self, bx, target); + return; + } + _ => FnType::new(bx.cx, sig, &extra_args) + }; + + // The arguments we'll be passing. Plus one to account for outptr, if used. + let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; + let mut llargs = Vec::with_capacity(arg_count); + + // Prepare the return value destination + let ret_dest = if let Some((ref dest, _)) = *destination { + let is_intrinsic = intrinsic.is_some(); + self.make_return_dest(&bx, dest, &fn_ty.ret, &mut llargs, + is_intrinsic) + } else { + ReturnDest::Nothing + }; + + if intrinsic.is_some() && intrinsic != Some("drop_in_place") { + use intrinsic::codegen_intrinsic_call; + + let dest = match ret_dest { + _ if fn_ty.ret.is_indirect() => llargs[0], + ReturnDest::Nothing => { + C_undef(fn_ty.ret.memory_ty(bx.cx).ptr_to()) + } + ReturnDest::IndirectOperand(dst, _) | + ReturnDest::Store(dst) => dst.llval, + ReturnDest::DirectOperand(_) => + bug!("Cannot use direct operand with an intrinsic call") + }; + + let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { + // The indices passed to simd_shuffle* in the + // third argument must be constant. This is + // checked by const-qualification, which also + // promotes any complex rvalues to constants. + if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { + match *arg { + mir::Operand::Copy(_) | + mir::Operand::Move(_) => { + span_bug!(span, "shuffle indices must be constant"); + } + mir::Operand::Constant(ref constant) => { + let (llval, ty) = self.simd_shuffle_indices( + &bx, + constant, + ); + return OperandRef { + val: Immediate(llval), + layout: bx.cx.layout_of(ty) + }; + } + } + } + + self.codegen_operand(&bx, arg) + }).collect(); + + + let callee_ty = instance.as_ref().unwrap().ty(bx.cx.tcx); + codegen_intrinsic_call(&bx, callee_ty, &fn_ty, &args, dest, + terminator.source_info.span); + + if let ReturnDest::IndirectOperand(dst, _) = ret_dest { + self.store_return(&bx, ret_dest, &fn_ty.ret, dst.llval); + } + + if let Some((_, target)) = *destination { + funclet_br(self, bx, target); + } else { + bx.unreachable(); + } + + return; + } + + // Split the rust-call tupled arguments off. + let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { + let (tup, args) = args.split_last().unwrap(); + (args, Some(tup)) + } else { + (&args[..], None) + }; + + for (i, arg) in first_args.iter().enumerate() { + let mut op = self.codegen_operand(&bx, arg); + if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { + if let Pair(data_ptr, meta) = op.val { + llfn = Some(meth::VirtualIndex::from_index(idx) + .get_fn(&bx, meta, &fn_ty)); + llargs.push(data_ptr); + continue; + } + } + + // The callee needs to own the argument memory if we pass it + // by-ref, so make a local copy of non-immediate constants. + match (arg, op.val) { + (&mir::Operand::Copy(_), Ref(..)) | + (&mir::Operand::Constant(_), Ref(..)) => { + let tmp = PlaceRef::alloca(&bx, op.layout, "const"); + op.val.store(&bx, tmp); + op.val = Ref(tmp.llval, tmp.align); + } + _ => {} + } + + self.codegen_argument(&bx, op, &mut llargs, &fn_ty.args[i]); + } + if let Some(tup) = untuple { + self.codegen_arguments_untupled(&bx, tup, &mut llargs, + &fn_ty.args[first_args.len()..]) + } + + let fn_ptr = match (llfn, instance) { + (Some(llfn), _) => llfn, + (None, Some(instance)) => callee::get_fn(bx.cx, instance), + _ => span_bug!(span, "no llfn for call"), + }; + + do_call(self, bx, fn_ty, fn_ptr, &llargs, + destination.as_ref().map(|&(_, target)| (ret_dest, target)), + cleanup); + } + mir::TerminatorKind::GeneratorDrop | + mir::TerminatorKind::Yield { .. } => bug!("generator ops in codegen"), + mir::TerminatorKind::FalseEdges { .. } | + mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in codegen"), + } + } + + fn codegen_argument(&mut self, + bx: &Builder<'a, 'tcx>, + op: OperandRef<'tcx>, + llargs: &mut Vec, + arg: &ArgType<'tcx, Ty<'tcx>>) { + // Fill padding with undef value, where applicable. + if let Some(ty) = arg.pad { + llargs.push(C_undef(ty.llvm_type(bx.cx))); + } + + if arg.is_ignore() { + return; + } + + if let PassMode::Pair(..) = arg.mode { + match op.val { + Pair(a, b) => { + llargs.push(a); + llargs.push(b); + return; + } + _ => bug!("codegen_argument: {:?} invalid for pair arugment", op) + } + } + + // Force by-ref if we have to load through a cast pointer. + let (mut llval, align, by_ref) = match op.val { + Immediate(_) | Pair(..) => { + match arg.mode { + PassMode::Indirect(_) | PassMode::Cast(_) => { + let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); + op.val.store(bx, scratch); + (scratch.llval, scratch.align, true) + } + _ => { + (op.immediate_or_packed_pair(bx), arg.layout.align, false) + } + } + } + Ref(llval, align) => { + if arg.is_indirect() && align.abi() < arg.layout.align.abi() { + // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I + // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't + // have scary latent bugs around. + + let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); + base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty()); + (scratch.llval, scratch.align, true) + } else { + (llval, align, true) + } + } + }; + + if by_ref && !arg.is_indirect() { + // Have to load the argument, maybe while casting it. + if let PassMode::Cast(ty) = arg.mode { + llval = bx.load(bx.pointercast(llval, ty.llvm_type(bx.cx).ptr_to()), + align.min(arg.layout.align)); + } else { + // We can't use `PlaceRef::load` here because the argument + // may have a type we don't treat as immediate, but the ABI + // used for this call is passing it by-value. In that case, + // the load would just produce `OperandValue::Ref` instead + // of the `OperandValue::Immediate` we need for the call. + llval = bx.load(llval, align); + if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { + if scalar.is_bool() { + bx.range_metadata(llval, 0..2); + } + } + // We store bools as i8 so we need to truncate to i1. + llval = base::to_immediate(bx, llval, arg.layout); + } + } + + llargs.push(llval); + } + + fn codegen_arguments_untupled(&mut self, + bx: &Builder<'a, 'tcx>, + operand: &mir::Operand<'tcx>, + llargs: &mut Vec, + args: &[ArgType<'tcx, Ty<'tcx>>]) { + let tuple = self.codegen_operand(bx, operand); + + // Handle both by-ref and immediate tuples. + if let Ref(llval, align) = tuple.val { + let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align); + for i in 0..tuple.layout.fields.count() { + let field_ptr = tuple_ptr.project_field(bx, i); + self.codegen_argument(bx, field_ptr.load(bx), llargs, &args[i]); + } + } else { + // If the tuple is immediate, the elements are as well. + for i in 0..tuple.layout.fields.count() { + let op = tuple.extract_field(bx, i); + self.codegen_argument(bx, op, llargs, &args[i]); + } + } + } + + fn get_personality_slot(&mut self, bx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> { + let cx = bx.cx; + if let Some(slot) = self.personality_slot { + slot + } else { + let layout = cx.layout_of(cx.tcx.intern_tup(&[ + cx.tcx.mk_mut_ptr(cx.tcx.types.u8), + cx.tcx.types.i32 + ])); + let slot = PlaceRef::alloca(bx, layout, "personalityslot"); + self.personality_slot = Some(slot); + slot + } + } + + /// Return the landingpad wrapper around the given basic block + /// + /// No-op in MSVC SEH scheme. + fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef { + if let Some(block) = self.landing_pads[target_bb] { + return block; + } + + let block = self.blocks[target_bb]; + let landing_pad = self.landing_pad_uncached(block); + self.landing_pads[target_bb] = Some(landing_pad); + landing_pad + } + + fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { + if base::wants_msvc_seh(self.cx.sess()) { + span_bug!(self.mir.span, "landing pad was not inserted?") + } + + let bx = self.new_block("cleanup"); + + let llpersonality = self.cx.eh_personality(); + let llretty = self.landing_pad_type(); + let lp = bx.landing_pad(llretty, llpersonality, 1); + bx.set_cleanup(lp); + + let slot = self.get_personality_slot(&bx); + slot.storage_live(&bx); + Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&bx, slot); + + bx.br(target_bb); + bx.llbb() + } + + fn landing_pad_type(&self) -> Type { + let cx = self.cx; + Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], false) + } + + fn unreachable_block(&mut self) -> BasicBlockRef { + self.unreachable_block.unwrap_or_else(|| { + let bl = self.new_block("unreachable"); + bl.unreachable(); + self.unreachable_block = Some(bl.llbb()); + bl.llbb() + }) + } + + pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> { + Builder::new_block(self.cx, self.llfn, name) + } + + pub fn build_block(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> { + let bx = Builder::with_cx(self.cx); + bx.position_at_end(self.blocks[bb]); + bx + } + + fn make_return_dest(&mut self, bx: &Builder<'a, 'tcx>, + dest: &mir::Place<'tcx>, fn_ret: &ArgType<'tcx, Ty<'tcx>>, + llargs: &mut Vec, is_intrinsic: bool) + -> ReturnDest<'tcx> { + // If the return is ignored, we can just return a do-nothing ReturnDest + if fn_ret.is_ignore() { + return ReturnDest::Nothing; + } + let dest = if let mir::Place::Local(index) = *dest { + match self.locals[index] { + LocalRef::Place(dest) => dest, + LocalRef::Operand(None) => { + // Handle temporary places, specifically Operand ones, as + // they don't have allocas + return if fn_ret.is_indirect() { + // Odd, but possible, case, we have an operand temporary, + // but the calling convention has an indirect return. + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + llargs.push(tmp.llval); + ReturnDest::IndirectOperand(tmp, index) + } else if is_intrinsic { + // Currently, intrinsics always need a location to store + // the result. so we create a temporary alloca for the + // result + let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); + tmp.storage_live(bx); + ReturnDest::IndirectOperand(tmp, index) + } else { + ReturnDest::DirectOperand(index) + }; + } + LocalRef::Operand(Some(_)) => { + bug!("place local already assigned to"); + } + } + } else { + self.codegen_place(bx, dest) + }; + if fn_ret.is_indirect() { + if dest.align.abi() < dest.layout.align.abi() { + // Currently, MIR code generation does not create calls + // that store directly to fields of packed structs (in + // fact, the calls it creates write only to temps), + // + // If someone changes that, please update this code path + // to create a temporary. + span_bug!(self.mir.span, "can't directly store to unaligned value"); + } + llargs.push(dest.llval); + ReturnDest::Nothing + } else { + ReturnDest::Store(dest) + } + } + + fn codegen_transmute(&mut self, bx: &Builder<'a, 'tcx>, + src: &mir::Operand<'tcx>, + dst: &mir::Place<'tcx>) { + if let mir::Place::Local(index) = *dst { + match self.locals[index] { + LocalRef::Place(place) => self.codegen_transmute_into(bx, src, place), + LocalRef::Operand(None) => { + let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); + assert!(!dst_layout.ty.has_erasable_regions()); + let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); + place.storage_live(bx); + self.codegen_transmute_into(bx, src, place); + let op = place.load(bx); + place.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + } + LocalRef::Operand(Some(op)) => { + assert!(op.layout.is_zst(), + "assigning to initialized SSAtemp"); + } + } + } else { + let dst = self.codegen_place(bx, dst); + self.codegen_transmute_into(bx, src, dst); + } + } + + fn codegen_transmute_into(&mut self, bx: &Builder<'a, 'tcx>, + src: &mir::Operand<'tcx>, + dst: PlaceRef<'tcx>) { + let src = self.codegen_operand(bx, src); + let llty = src.layout.llvm_type(bx.cx); + let cast_ptr = bx.pointercast(dst.llval, llty.ptr_to()); + let align = src.layout.align.min(dst.layout.align); + src.val.store(bx, PlaceRef::new_sized(cast_ptr, src.layout, align)); + } + + + // Stores the return value of a function call into it's final location. + fn store_return(&mut self, + bx: &Builder<'a, 'tcx>, + dest: ReturnDest<'tcx>, + ret_ty: &ArgType<'tcx, Ty<'tcx>>, + llval: ValueRef) { + use self::ReturnDest::*; + + match dest { + Nothing => (), + Store(dst) => ret_ty.store(bx, llval, dst), + IndirectOperand(tmp, index) => { + let op = tmp.load(bx); + tmp.storage_dead(bx); + self.locals[index] = LocalRef::Operand(Some(op)); + } + DirectOperand(index) => { + // If there is a cast, we have to store and reload. + let op = if let PassMode::Cast(_) = ret_ty.mode { + let tmp = PlaceRef::alloca(bx, ret_ty.layout, "tmp_ret"); + tmp.storage_live(bx); + ret_ty.store(bx, llval, tmp); + let op = tmp.load(bx); + tmp.storage_dead(bx); + op + } else { + OperandRef::from_immediate_or_packed_pair(bx, llval, ret_ty.layout) + }; + self.locals[index] = LocalRef::Operand(Some(op)); + } + } + } +} + +enum ReturnDest<'tcx> { + // Do nothing, the return value is indirect or ignored + Nothing, + // Store the return value to the pointer + Store(PlaceRef<'tcx>), + // Stores an indirect return value to an operand local place + IndirectOperand(PlaceRef<'tcx>, mir::Local), + // Stores a direct return value to an operand local place + DirectOperand(mir::Local) +} diff --git a/src/librustc_codegen_llvm/mir/constant.rs b/src/librustc_codegen_llvm/mir/constant.rs new file mode 100644 index 00000000000..c2638d2d410 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/constant.rs @@ -0,0 +1,271 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::middle::const_val::{ConstVal, ConstEvalErr}; +use rustc_mir::interpret::{read_target_uint, const_val_field}; +use rustc::hir::def_id::DefId; +use rustc::mir; +use rustc_data_structures::indexed_vec::Idx; +use rustc::mir::interpret::{GlobalId, MemoryPointer, PrimVal, Allocation, ConstValue}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; +use builder::Builder; +use common::{CodegenCx}; +use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; +use consts; +use type_of::LayoutLlvmExt; +use type_::Type; +use syntax::ast::Mutability; + +use super::super::callee; +use super::FunctionCx; + +pub fn primval_to_llvm(cx: &CodegenCx, + cv: PrimVal, + scalar: &Scalar, + llty: Type) -> ValueRef { + let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; + match cv { + PrimVal::Undef => C_undef(Type::ix(cx, bits)), + PrimVal::Bytes(b) => { + let llval = C_uint_big(Type::ix(cx, bits), b); + if scalar.value == layout::Pointer { + unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) + } + }, + PrimVal::Ptr(ptr) => { + if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { + callee::get_fn(cx, fn_instance) + } else { + let static_ = cx + .tcx + .interpret_interner + .get_static(ptr.alloc_id); + let base_addr = if let Some(def_id) = static_ { + assert!(cx.tcx.is_static(def_id).is_some()); + consts::get_static(cx, def_id) + } else if let Some(alloc) = cx.tcx.interpret_interner + .get_alloc(ptr.alloc_id) { + let init = const_alloc_to_llvm(cx, alloc); + if alloc.runtime_mutability == Mutability::Mutable { + consts::addr_of_mut(cx, init, alloc.align, "byte_str") + } else { + consts::addr_of(cx, init, alloc.align, "byte_str") + } + } else { + bug!("missing allocation {:?}", ptr.alloc_id); + }; + + let llval = unsafe { llvm::LLVMConstInBoundsGEP( + consts::bitcast(base_addr, Type::i8p(cx)), + &C_usize(cx, ptr.offset), + 1, + ) }; + if scalar.value != layout::Pointer { + unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } + } else { + consts::bitcast(llval, llty) + } + } + } + } +} + +fn const_value_to_llvm<'tcx>(cx: &CodegenCx<'_, 'tcx>, val: ConstValue, ty: Ty<'tcx>) -> ValueRef { + let layout = cx.layout_of(ty); + + if layout.is_zst() { + return C_undef(layout.immediate_llvm_type(cx)); + } + + match val { + ConstValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("const_value_to_llvm: invalid ByVal layout: {:#?}", layout) + }; + primval_to_llvm( + cx, + x, + scalar, + layout.immediate_llvm_type(cx), + ) + }, + ConstValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("const_value_to_llvm: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(cx, 0), + ); + let b_llval = primval_to_llvm( + cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(cx, 1), + ); + C_struct(cx, &[a_llval, b_llval], false) + }, + ConstValue::ByRef(alloc) => const_alloc_to_llvm(cx, alloc), + } +} + +pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { + let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); + let layout = cx.data_layout(); + let pointer_size = layout.pointer_size.bytes() as usize; + + let mut next_offset = 0; + for (&offset, &alloc_id) in &alloc.relocations { + assert_eq!(offset as usize as u64, offset); + let offset = offset as usize; + if offset > next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); + } + let ptr_offset = read_target_uint( + layout.endian, + &alloc.bytes[offset..(offset + pointer_size)], + ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; + llvals.push(primval_to_llvm( + cx, + PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), + &Scalar { + value: layout::Primitive::Pointer, + valid_range: 0..=!0 + }, + Type::i8p(cx) + )); + next_offset = offset + pointer_size; + } + if alloc.bytes.len() >= next_offset { + llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); + } + + C_struct(cx, &llvals, true) +} + +pub fn codegen_static_initializer<'a, 'tcx>( + cx: &CodegenCx<'a, 'tcx>, + def_id: DefId) + -> Result> +{ + let instance = ty::Instance::mono(cx.tcx, def_id); + let cid = GlobalId { + instance, + promoted: None + }; + let param_env = ty::ParamEnv::reveal_all(); + let static_ = cx.tcx.const_eval(param_env.and(cid))?; + + let val = match static_.val { + ConstVal::Value(val) => val, + _ => bug!("static const eval returned {:#?}", static_), + }; + Ok(const_value_to_llvm(cx, val, static_.ty)) +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn const_to_const_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &'tcx ty::Const<'tcx>, + ) -> Result, ConstEvalErr<'tcx>> { + match constant.val { + ConstVal::Unevaluated(def_id, ref substs) => { + let tcx = bx.tcx(); + let param_env = ty::ParamEnv::reveal_all(); + let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); + let cid = GlobalId { + instance, + promoted: None, + }; + let c = tcx.const_eval(param_env.and(cid))?; + self.const_to_const_value(bx, c) + }, + ConstVal::Value(val) => Ok(val), + } + } + + pub fn mir_constant_to_const_value( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> Result, ConstEvalErr<'tcx>> { + match constant.literal { + mir::Literal::Promoted { index } => { + let param_env = ty::ParamEnv::reveal_all(); + let cid = mir::interpret::GlobalId { + instance: self.instance, + promoted: Some(index), + }; + bx.tcx().const_eval(param_env.and(cid)) + } + mir::Literal::Value { value } => { + Ok(self.monomorphize(&value)) + } + }.and_then(|c| self.const_to_const_value(bx, c)) + } + + /// process constant containing SIMD shuffle indices + pub fn simd_shuffle_indices( + &mut self, + bx: &Builder<'a, 'tcx>, + constant: &mir::Constant<'tcx>, + ) -> (ValueRef, Ty<'tcx>) { + self.mir_constant_to_const_value(bx, constant) + .and_then(|c| { + let field_ty = constant.ty.builtin_index().unwrap(); + let fields = match constant.ty.sty { + ty::TyArray(_, n) => n.unwrap_usize(bx.tcx()), + ref other => bug!("invalid simd shuffle type: {}", other), + }; + let values: Result, _> = (0..fields).map(|field| { + let field = const_val_field( + bx.tcx(), + ty::ParamEnv::reveal_all(), + self.instance, + None, + mir::Field::new(field as usize), + c, + constant.ty, + )?; + if let Some(prim) = field.to_primval() { + let layout = bx.cx.layout_of(field_ty); + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + Ok(primval_to_llvm( + bx.cx, prim, scalar, + layout.immediate_llvm_type(bx.cx), + )) + } else { + bug!("simd shuffle field {:?}", field) + } + }).collect(); + let llval = C_struct(bx.cx, &values?, false); + Ok((llval, constant.ty)) + }) + .unwrap_or_else(|e| { + e.report(bx.tcx(), constant.span, "shuffle_indices"); + // We've errored, so we don't have to produce working code. + let ty = self.monomorphize(&constant.ty); + let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); + (C_undef(llty), ty) + }) + } +} diff --git a/src/librustc_codegen_llvm/mir/mod.rs b/src/librustc_codegen_llvm/mir/mod.rs new file mode 100644 index 00000000000..47b15320311 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/mod.rs @@ -0,0 +1,652 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use common::{C_i32, C_null}; +use libc::c_uint; +use llvm::{self, ValueRef, BasicBlockRef}; +use llvm::debuginfo::DIScope; +use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts}; +use rustc::ty::layout::{LayoutOf, TyLayout}; +use rustc::mir::{self, Mir}; +use rustc::ty::subst::Substs; +use rustc::session::config::FullDebugInfo; +use base; +use builder::Builder; +use common::{CodegenCx, Funclet}; +use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; +use monomorphize::Instance; +use abi::{ArgAttribute, ArgTypeExt, FnType, FnTypeExt, PassMode}; +use type_::Type; + +use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; +use syntax::symbol::keywords; + +use std::iter; + +use rustc_data_structures::bitvec::BitVector; +use rustc_data_structures::indexed_vec::{IndexVec, Idx}; + +pub use self::constant::codegen_static_initializer; + +use self::analyze::CleanupKind; +use self::place::PlaceRef; +use rustc::mir::traversal; + +use self::operand::{OperandRef, OperandValue}; + +/// Master context for codegenning from MIR. +pub struct FunctionCx<'a, 'tcx:'a> { + instance: Instance<'tcx>, + + mir: &'a mir::Mir<'tcx>, + + debug_context: debuginfo::FunctionDebugContext, + + llfn: ValueRef, + + cx: &'a CodegenCx<'a, 'tcx>, + + fn_ty: FnType<'tcx, Ty<'tcx>>, + + /// When unwinding is initiated, we have to store this personality + /// value somewhere so that we can load it and re-use it in the + /// resume instruction. The personality is (afaik) some kind of + /// value used for C++ unwinding, which must filter by type: we + /// don't really care about it very much. Anyway, this value + /// contains an alloca into which the personality is stored and + /// then later loaded when generating the DIVERGE_BLOCK. + personality_slot: Option>, + + /// A `Block` for each MIR `BasicBlock` + blocks: IndexVec, + + /// The funclet status of each basic block + cleanup_kinds: IndexVec, + + /// When targeting MSVC, this stores the cleanup info for each funclet + /// BB. This is initialized as we compute the funclets' head block in RPO. + funclets: &'a IndexVec>, + + /// This stores the landing-pad block for a given BB, computed lazily on GNU + /// and eagerly on MSVC. + landing_pads: IndexVec>, + + /// Cached unreachable block + unreachable_block: Option, + + /// The location where each MIR arg/var/tmp/ret is stored. This is + /// usually an `PlaceRef` representing an alloca, but not always: + /// sometimes we can skip the alloca and just store the value + /// directly using an `OperandRef`, which makes for tighter LLVM + /// IR. The conditions for using an `OperandRef` are as follows: + /// + /// - the type of the local must be judged "immediate" by `is_llvm_immediate` + /// - the operand must never be referenced indirectly + /// - we should not take its address using the `&` operator + /// - nor should it appear in a place path like `tmp.a` + /// - the operand must be defined by an rvalue that can generate immediate + /// values + /// + /// Avoiding allocs can also be important for certain intrinsics, + /// notably `expect`. + locals: IndexVec>, + + /// Debug information for MIR scopes. + scopes: IndexVec, + + /// If this function is being monomorphized, this contains the type substitutions used. + param_substs: &'tcx Substs<'tcx>, +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn monomorphize(&self, value: &T) -> T + where T: TypeFoldable<'tcx> + { + self.cx.tcx.subst_and_normalize_erasing_regions( + self.param_substs, + ty::ParamEnv::reveal_all(), + value, + ) + } + + pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) { + let (scope, span) = self.debug_loc(source_info); + debuginfo::set_source_location(&self.debug_context, bx, scope, span); + } + + pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) { + // Bail out if debug info emission is not enabled. + match self.debug_context { + FunctionDebugContext::DebugInfoDisabled | + FunctionDebugContext::FunctionWithoutDebugInfo => { + return (self.scopes[source_info.scope].scope_metadata, source_info.span); + } + FunctionDebugContext::RegularContext(_) =>{} + } + + // In order to have a good line stepping behavior in debugger, we overwrite debug + // locations of macro expansions with that of the outermost expansion site + // (unless the crate is being compiled with `-Z debug-macros`). + if source_info.span.ctxt() == NO_EXPANSION || + self.cx.sess().opts.debugging_opts.debug_macros { + let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); + (scope, source_info.span) + } else { + // Walk up the macro expansion chain until we reach a non-expanded span. + // We also stop at the function body level because no line stepping can occur + // at the level above that. + let mut span = source_info.span; + while span.ctxt() != NO_EXPANSION && span.ctxt() != self.mir.span.ctxt() { + if let Some(info) = span.ctxt().outer().expn_info() { + span = info.call_site; + } else { + break; + } + } + let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); + // Use span of the outermost expansion site, while keeping the original lexical scope. + (scope, span) + } + } + + // DILocations inherit source file name from the parent DIScope. Due to macro expansions + // it may so happen that the current span belongs to a different file than the DIScope + // corresponding to span's containing visibility scope. If so, we need to create a DIScope + // "extension" into that file. + fn scope_metadata_for_loc(&self, scope_id: mir::VisibilityScope, pos: BytePos) + -> llvm::debuginfo::DIScope { + let scope_metadata = self.scopes[scope_id].scope_metadata; + if pos < self.scopes[scope_id].file_start_pos || + pos >= self.scopes[scope_id].file_end_pos { + let cm = self.cx.sess().codemap(); + let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; + debuginfo::extend_scope_to_file(self.cx, + scope_metadata, + &cm.lookup_char_pos(pos).file, + defining_crate) + } else { + scope_metadata + } + } +} + +enum LocalRef<'tcx> { + Place(PlaceRef<'tcx>), + Operand(Option>), +} + +impl<'a, 'tcx> LocalRef<'tcx> { + fn new_operand(cx: &CodegenCx<'a, 'tcx>, layout: TyLayout<'tcx>) -> LocalRef<'tcx> { + if layout.is_zst() { + // Zero-size temporaries aren't always initialized, which + // doesn't matter because they don't contain data, but + // we need something in the operand. + LocalRef::Operand(Some(OperandRef::new_zst(cx, layout))) + } else { + LocalRef::Operand(None) + } + } +} + +/////////////////////////////////////////////////////////////////////////// + +pub fn codegen_mir<'a, 'tcx: 'a>( + cx: &'a CodegenCx<'a, 'tcx>, + llfn: ValueRef, + mir: &'a Mir<'tcx>, + instance: Instance<'tcx>, + sig: ty::FnSig<'tcx>, +) { + let fn_ty = FnType::new(cx, sig, &[]); + debug!("fn_ty: {:?}", fn_ty); + let debug_context = + debuginfo::create_function_debug_context(cx, instance, sig, llfn, mir); + let bx = Builder::new_block(cx, llfn, "start"); + + if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { + bx.set_personality_fn(cx.eh_personality()); + } + + let cleanup_kinds = analyze::cleanup_kinds(&mir); + // Allocate a `Block` for every basic block, except + // the start block, if nothing loops back to it. + let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); + let block_bxs: IndexVec = + mir.basic_blocks().indices().map(|bb| { + if bb == mir::START_BLOCK && !reentrant_start_block { + bx.llbb() + } else { + bx.build_sibling_block(&format!("{:?}", bb)).llbb() + } + }).collect(); + + // Compute debuginfo scopes from MIR scopes. + let scopes = debuginfo::create_mir_scopes(cx, mir, &debug_context); + let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs); + + let mut fx = FunctionCx { + instance, + mir, + llfn, + fn_ty, + cx, + personality_slot: None, + blocks: block_bxs, + unreachable_block: None, + cleanup_kinds, + landing_pads, + funclets: &funclets, + scopes, + locals: IndexVec::new(), + debug_context, + param_substs: { + assert!(!instance.substs.needs_infer()); + instance.substs + }, + }; + + let memory_locals = analyze::non_ssa_locals(&fx); + + // Allocate variable and temp allocas + fx.locals = { + let args = arg_local_refs(&bx, &fx, &fx.scopes, &memory_locals); + + let mut allocate_local = |local| { + let decl = &mir.local_decls[local]; + let layout = bx.cx.layout_of(fx.monomorphize(&decl.ty)); + assert!(!layout.ty.has_erasable_regions()); + + if let Some(name) = decl.name { + // User variable + let debug_scope = fx.scopes[decl.source_info.scope]; + let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo; + + if !memory_locals.contains(local.index()) && !dbg { + debug!("alloc: {:?} ({}) -> operand", local, name); + return LocalRef::new_operand(bx.cx, layout); + } + + debug!("alloc: {:?} ({}) -> place", local, name); + let place = PlaceRef::alloca(&bx, layout, &name.as_str()); + if dbg { + let (scope, span) = fx.debug_loc(decl.source_info); + declare_local(&bx, &fx.debug_context, name, layout.ty, scope, + VariableAccess::DirectVariable { alloca: place.llval }, + VariableKind::LocalVariable, span); + } + LocalRef::Place(place) + } else { + // Temporary or return place + if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { + debug!("alloc: {:?} (return place) -> place", local); + let llretptr = llvm::get_param(llfn, 0); + LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align)) + } else if memory_locals.contains(local.index()) { + debug!("alloc: {:?} -> place", local); + LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local))) + } else { + // If this is an immediate local, we do not create an + // alloca in advance. Instead we wait until we see the + // definition and update the operand there. + debug!("alloc: {:?} -> operand", local); + LocalRef::new_operand(bx.cx, layout) + } + } + }; + + let retptr = allocate_local(mir::RETURN_PLACE); + iter::once(retptr) + .chain(args.into_iter()) + .chain(mir.vars_and_temps_iter().map(allocate_local)) + .collect() + }; + + // Branch to the START block, if it's not the entry block. + if reentrant_start_block { + bx.br(fx.blocks[mir::START_BLOCK]); + } + + // Up until here, IR instructions for this function have explicitly not been annotated with + // source code location, so we don't step into call setup code. From here on, source location + // emitting should be enabled. + debuginfo::start_emitting_source_locations(&fx.debug_context); + + let rpo = traversal::reverse_postorder(&mir); + let mut visited = BitVector::new(mir.basic_blocks().len()); + + // Codegen the body of each block using reverse postorder + for (bb, _) in rpo { + visited.insert(bb.index()); + fx.codegen_block(bb); + } + + // Remove blocks that haven't been visited, or have no + // predecessors. + for bb in mir.basic_blocks().indices() { + // Unreachable block + if !visited.contains(bb.index()) { + debug!("codegen_mir: block {:?} was not visited", bb); + unsafe { + llvm::LLVMDeleteBasicBlock(fx.blocks[bb]); + } + } + } +} + +fn create_funclets<'a, 'tcx>( + mir: &'a Mir<'tcx>, + bx: &Builder<'a, 'tcx>, + cleanup_kinds: &IndexVec, + block_bxs: &IndexVec) + -> (IndexVec>, + IndexVec>) +{ + block_bxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| { + match *cleanup_kind { + CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} + _ => return (None, None) + } + + let cleanup; + let ret_llbb; + match mir[bb].terminator.as_ref().map(|t| &t.kind) { + // This is a basic block that we're aborting the program for, + // notably in an `extern` function. These basic blocks are inserted + // so that we assert that `extern` functions do indeed not panic, + // and if they do we abort the process. + // + // On MSVC these are tricky though (where we're doing funclets). If + // we were to do a cleanuppad (like below) the normal functions like + // `longjmp` would trigger the abort logic, terminating the + // program. Instead we insert the equivalent of `catch(...)` for C++ + // which magically doesn't trigger when `longjmp` files over this + // frame. + // + // Lots more discussion can be found on #48251 but this codegen is + // modeled after clang's for: + // + // try { + // foo(); + // } catch (...) { + // bar(); + // } + Some(&mir::TerminatorKind::Abort) => { + let cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); + let cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); + ret_llbb = cs_bx.llbb(); + + let cs = cs_bx.catch_switch(None, None, 1); + cs_bx.add_handler(cs, cp_bx.llbb()); + + // The "null" here is actually a RTTI type descriptor for the + // C++ personality function, but `catch (...)` has no type so + // it's null. The 64 here is actually a bitfield which + // represents that this is a catch-all block. + let null = C_null(Type::i8p(bx.cx)); + let sixty_four = C_i32(bx.cx, 64); + cleanup = cp_bx.catch_pad(cs, &[null, sixty_four, null]); + cp_bx.br(llbb); + } + _ => { + let cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); + ret_llbb = cleanup_bx.llbb(); + cleanup = cleanup_bx.cleanup_pad(None, &[]); + cleanup_bx.br(llbb); + } + }; + + (Some(ret_llbb), Some(Funclet::new(cleanup))) + }).unzip() +} + +/// Produce, for each argument, a `ValueRef` pointing at the +/// argument's value. As arguments are places, these are always +/// indirect. +fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, + fx: &FunctionCx<'a, 'tcx>, + scopes: &IndexVec, + memory_locals: &BitVector) + -> Vec> { + let mir = fx.mir; + let tcx = bx.tcx(); + let mut idx = 0; + let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; + + // Get the argument scope, if it exists and if we need it. + let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; + let arg_scope = if arg_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo { + Some(arg_scope.scope_metadata) + } else { + None + }; + + let deref_op = unsafe { + [llvm::LLVMRustDIBuilderCreateOpDeref()] + }; + + mir.args_iter().enumerate().map(|(arg_index, local)| { + let arg_decl = &mir.local_decls[local]; + + let name = if let Some(name) = arg_decl.name { + name.as_str().to_string() + } else { + format!("arg{}", arg_index) + }; + + if Some(local) == mir.spread_arg { + // This argument (e.g. the last argument in the "rust-call" ABI) + // is a tuple that was spread at the ABI level and now we have + // to reconstruct it into a tuple local variable, from multiple + // individual LLVM function arguments. + + let arg_ty = fx.monomorphize(&arg_decl.ty); + let tupled_arg_tys = match arg_ty.sty { + ty::TyTuple(ref tys) => tys, + _ => bug!("spread argument isn't a tuple?!") + }; + + let place = PlaceRef::alloca(bx, bx.cx.layout_of(arg_ty), &name); + for i in 0..tupled_arg_tys.len() { + let arg = &fx.fn_ty.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + arg.store_fn_arg(bx, &mut llarg_idx, place.project_field(bx, i)); + } + + // Now that we have one alloca that contains the aggregate value, + // we can create one debuginfo entry for the argument. + arg_scope.map(|scope| { + let variable_access = VariableAccess::DirectVariable { + alloca: place.llval + }; + declare_local( + bx, + &fx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg_ty, scope, + variable_access, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); + }); + + return LocalRef::Place(place); + } + + let arg = &fx.fn_ty.args[idx]; + idx += 1; + if arg.pad.is_some() { + llarg_idx += 1; + } + + if arg_scope.is_none() && !memory_locals.contains(local.index()) { + // We don't have to cast or keep the argument in the alloca. + // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead + // of putting everything in allocas just so we can use llvm.dbg.declare. + let local = |op| LocalRef::Operand(Some(op)); + match arg.mode { + PassMode::Ignore => { + return local(OperandRef::new_zst(bx.cx, arg.layout)); + } + PassMode::Direct(_) => { + let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(llarg, &name); + llarg_idx += 1; + return local( + OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout)); + } + PassMode::Pair(..) => { + let a = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(a, &(name.clone() + ".0")); + llarg_idx += 1; + + let b = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(b, &(name + ".1")); + llarg_idx += 1; + + return local(OperandRef { + val: OperandValue::Pair(a, b), + layout: arg.layout + }); + } + _ => {} + } + } + + let place = if arg.is_indirect() { + // Don't copy an indirect argument to an alloca, the caller + // already put it in a temporary alloca and gave it up. + // FIXME: lifetimes + let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); + bx.set_value_name(llarg, &name); + llarg_idx += 1; + PlaceRef::new_sized(llarg, arg.layout, arg.layout.align) + } else { + let tmp = PlaceRef::alloca(bx, arg.layout, &name); + arg.store_fn_arg(bx, &mut llarg_idx, tmp); + tmp + }; + arg_scope.map(|scope| { + // Is this a regular argument? + if arg_index > 0 || mir.upvar_decls.is_empty() { + // The Rust ABI passes indirect variables using a pointer and a manual copy, so we + // need to insert a deref here, but the C ABI uses a pointer and a copy using the + // byval attribute, for which LLVM does the deref itself, so we must not add it. + // Starting with D31439 in LLVM 5, it *always* does the deref itself. + let mut variable_access = VariableAccess::DirectVariable { + alloca: place.llval + }; + if unsafe { llvm::LLVMRustVersionMajor() < 5 } { + if let PassMode::Indirect(ref attrs) = arg.mode { + if !attrs.contains(ArgAttribute::ByVal) { + variable_access = VariableAccess::IndirectVariable { + alloca: place.llval, + address_operations: &deref_op, + }; + } + } + } + + declare_local( + bx, + &fx.debug_context, + arg_decl.name.unwrap_or(keywords::Invalid.name()), + arg.layout.ty, + scope, + variable_access, + VariableKind::ArgumentVariable(arg_index + 1), + DUMMY_SP + ); + return; + } + + // Or is it the closure environment? + let (closure_layout, env_ref) = match arg.layout.ty.sty { + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | + ty::TyRef(_, ty, _) => (bx.cx.layout_of(ty), true), + _ => (arg.layout, false) + }; + + let (def_id, upvar_substs) = match closure_layout.ty.sty { + ty::TyClosure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), + ty::TyGenerator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), + _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_layout.ty) + }; + let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); + + // Store the pointer to closure data in an alloca for debuginfo + // because that's what the llvm.dbg.declare intrinsic expects. + + // FIXME(eddyb) this shouldn't be necessary but SROA seems to + // mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it + // doesn't actually strip the offset when splitting the closure + // environment into its components so it ends up out of bounds. + let env_ptr = if !env_ref { + let scratch = PlaceRef::alloca(bx, + bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), + "__debuginfo_env_ptr"); + bx.store(place.llval, scratch.llval, scratch.align); + scratch.llval + } else { + place.llval + }; + + for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { + let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); + + let ops = unsafe { + [llvm::LLVMRustDIBuilderCreateOpDeref(), + llvm::LLVMRustDIBuilderCreateOpPlusUconst(), + byte_offset_of_var_in_env as i64, + llvm::LLVMRustDIBuilderCreateOpDeref()] + }; + + // The environment and the capture can each be indirect. + + // FIXME(eddyb) see above why we have to keep + // a pointer in an alloca for debuginfo atm. + let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] }; + + let ty = if let (true, &ty::TyRef(_, ty, _)) = (decl.by_ref, &ty.sty) { + ty + } else { + ops = &ops[..ops.len() - 1]; + ty + }; + + let variable_access = VariableAccess::IndirectVariable { + alloca: env_ptr, + address_operations: &ops + }; + declare_local( + bx, + &fx.debug_context, + decl.debug_name, + ty, + scope, + variable_access, + VariableKind::CapturedVariable, + DUMMY_SP + ); + } + }); + LocalRef::Place(place) + }).collect() +} + +mod analyze; +mod block; +mod constant; +pub mod place; +pub mod operand; +mod rvalue; +mod statement; diff --git a/src/librustc_codegen_llvm/mir/operand.rs b/src/librustc_codegen_llvm/mir/operand.rs new file mode 100644 index 00000000000..62ef58f8255 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/operand.rs @@ -0,0 +1,427 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::ValueRef; +use rustc::middle::const_val::ConstEvalErr; +use rustc::mir; +use rustc::mir::interpret::ConstValue; +use rustc::ty; +use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; +use rustc_data_structures::indexed_vec::Idx; + +use base; +use common::{self, CodegenCx, C_null, C_undef, C_usize}; +use builder::{Builder, MemFlags}; +use value::Value; +use type_of::LayoutLlvmExt; +use type_::Type; +use consts; + +use std::fmt; +use std::ptr; + +use super::{FunctionCx, LocalRef}; +use super::constant::{primval_to_llvm, const_alloc_to_llvm}; +use super::place::PlaceRef; + +/// The representation of a Rust value. The enum variant is in fact +/// uniquely determined by the value's type, but is kept as a +/// safety check. +#[derive(Copy, Clone)] +pub enum OperandValue { + /// A reference to the actual operand. The data is guaranteed + /// to be valid for the operand's lifetime. + Ref(ValueRef, Align), + /// A single LLVM value. + Immediate(ValueRef), + /// A pair of immediate LLVM values. Used by fat pointers too. + Pair(ValueRef, ValueRef) +} + +impl fmt::Debug for OperandValue { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + OperandValue::Ref(r, align) => { + write!(f, "Ref({:?}, {:?})", Value(r), align) + } + OperandValue::Immediate(i) => { + write!(f, "Immediate({:?})", Value(i)) + } + OperandValue::Pair(a, b) => { + write!(f, "Pair({:?}, {:?})", Value(a), Value(b)) + } + } + } +} + +/// An `OperandRef` is an "SSA" reference to a Rust value, along with +/// its type. +/// +/// NOTE: unless you know a value's type exactly, you should not +/// generate LLVM opcodes acting on it and instead act via methods, +/// to avoid nasty edge cases. In particular, using `Builder::store` +/// directly is sure to cause problems -- use `OperandRef::store` +/// instead. +#[derive(Copy, Clone)] +pub struct OperandRef<'tcx> { + // The value. + pub val: OperandValue, + + // The layout of value, based on its Rust type. + pub layout: TyLayout<'tcx>, +} + +impl<'tcx> fmt::Debug for OperandRef<'tcx> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) + } +} + +impl<'a, 'tcx> OperandRef<'tcx> { + pub fn new_zst(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>) -> OperandRef<'tcx> { + assert!(layout.is_zst()); + OperandRef { + val: OperandValue::Immediate(C_undef(layout.immediate_llvm_type(cx))), + layout + } + } + + pub fn from_const(bx: &Builder<'a, 'tcx>, + val: ConstValue<'tcx>, + ty: ty::Ty<'tcx>) + -> Result, ConstEvalErr<'tcx>> { + let layout = bx.cx.layout_of(ty); + + if layout.is_zst() { + return Ok(OperandRef::new_zst(bx.cx, layout)); + } + + let val = match val { + ConstValue::ByVal(x) => { + let scalar = match layout.abi { + layout::Abi::Scalar(ref x) => x, + _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) + }; + let llval = primval_to_llvm( + bx.cx, + x, + scalar, + layout.immediate_llvm_type(bx.cx), + ); + OperandValue::Immediate(llval) + }, + ConstValue::ByValPair(a, b) => { + let (a_scalar, b_scalar) = match layout.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) + }; + let a_llval = primval_to_llvm( + bx.cx, + a, + a_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 0), + ); + let b_llval = primval_to_llvm( + bx.cx, + b, + b_scalar, + layout.scalar_pair_element_llvm_type(bx.cx, 1), + ); + OperandValue::Pair(a_llval, b_llval) + }, + ConstValue::ByRef(alloc) => { + let init = const_alloc_to_llvm(bx.cx, alloc); + let llval = consts::addr_of(bx.cx, init, layout.align, "byte_str"); + let llval = consts::bitcast(llval, layout.llvm_type(bx.cx).ptr_to()); + return Ok(PlaceRef::new_sized(llval, layout, alloc.align).load(bx)); + }, + }; + + Ok(OperandRef { + val, + layout + }) + } + + /// Asserts that this operand refers to a scalar and returns + /// a reference to its value. + pub fn immediate(self) -> ValueRef { + match self.val { + OperandValue::Immediate(s) => s, + _ => bug!("not immediate: {:?}", self) + } + } + + pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> { + let projected_ty = self.layout.ty.builtin_deref(true) + .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; + let (llptr, llextra) = match self.val { + OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), + OperandValue::Pair(llptr, llextra) => (llptr, llextra), + OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) + }; + let layout = cx.layout_of(projected_ty); + PlaceRef { + llval: llptr, + llextra, + layout, + align: layout.align, + } + } + + /// If this operand is a `Pair`, we return an aggregate with the two values. + /// For other cases, see `immediate`. + pub fn immediate_or_packed_pair(self, bx: &Builder<'a, 'tcx>) -> ValueRef { + if let OperandValue::Pair(a, b) = self.val { + let llty = self.layout.llvm_type(bx.cx); + debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", + self, llty); + // Reconstruct the immediate aggregate. + let mut llpair = C_undef(llty); + llpair = bx.insert_value(llpair, a, 0); + llpair = bx.insert_value(llpair, b, 1); + llpair + } else { + self.immediate() + } + } + + /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. + pub fn from_immediate_or_packed_pair(bx: &Builder<'a, 'tcx>, + llval: ValueRef, + layout: TyLayout<'tcx>) + -> OperandRef<'tcx> { + let val = if layout.is_llvm_scalar_pair() { + debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", + llval, layout); + + // Deconstruct the immediate aggregate. + OperandValue::Pair(bx.extract_value(llval, 0), + bx.extract_value(llval, 1)) + } else { + OperandValue::Immediate(llval) + }; + OperandRef { val, layout } + } + + pub fn extract_field(&self, bx: &Builder<'a, 'tcx>, i: usize) -> OperandRef<'tcx> { + let field = self.layout.field(bx.cx, i); + let offset = self.layout.fields.offset(i); + + let mut val = match (self.val, &self.layout.abi) { + // If the field is ZST, it has no data. + _ if field.is_zst() => { + return OperandRef::new_zst(bx.cx, field); + } + + // Newtype of a scalar, scalar pair or vector. + (OperandValue::Immediate(_), _) | + (OperandValue::Pair(..), _) if field.size == self.layout.size => { + assert_eq!(offset.bytes(), 0); + self.val + } + + // Extract a scalar component from a pair. + (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { + if offset.bytes() == 0 { + assert_eq!(field.size, a.value.size(bx.cx)); + OperandValue::Immediate(a_llval) + } else { + assert_eq!(offset, a.value.size(bx.cx) + .abi_align(b.value.align(bx.cx))); + assert_eq!(field.size, b.value.size(bx.cx)); + OperandValue::Immediate(b_llval) + } + } + + // `#[repr(simd)]` types are also immediate. + (OperandValue::Immediate(llval), &layout::Abi::Vector { .. }) => { + OperandValue::Immediate( + bx.extract_element(llval, C_usize(bx.cx, i as u64))) + } + + _ => bug!("OperandRef::extract_field({:?}): not applicable", self) + }; + + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + match val { + OperandValue::Immediate(ref mut llval) => { + *llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx)); + } + OperandValue::Pair(ref mut a, ref mut b) => { + *a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0)); + *b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1)); + } + OperandValue::Ref(..) => bug!() + } + + OperandRef { + val, + layout: field + } + } +} + +impl<'a, 'tcx> OperandValue { + pub fn store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::empty()); + } + + pub fn volatile_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::VOLATILE); + } + + pub fn nontemporal_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { + self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); + } + + fn store_with_flags(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>, flags: MemFlags) { + debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); + // Avoid generating stores of zero-sized values, because the only way to have a zero-sized + // value is through `undef`, and store itself is useless. + if dest.layout.is_zst() { + return; + } + match self { + OperandValue::Ref(r, source_align) => { + base::memcpy_ty(bx, dest.llval, r, dest.layout, + source_align.min(dest.align), flags) + } + OperandValue::Immediate(s) => { + let val = base::from_immediate(bx, s); + bx.store_with_flags(val, dest.llval, dest.align, flags); + } + OperandValue::Pair(a, b) => { + for (i, &x) in [a, b].iter().enumerate() { + let mut llptr = bx.struct_gep(dest.llval, i as u64); + // Make sure to always store i1 as i8. + if common::val_ty(x) == Type::i1(bx.cx) { + llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); + } + let val = base::from_immediate(bx, x); + bx.store_with_flags(val, llptr, dest.align, flags); + } + } + } + } +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + fn maybe_codegen_consume_direct(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> Option> + { + debug!("maybe_codegen_consume_direct(place={:?})", place); + + // watch out for locals that do not have an + // alloca; they are handled somewhat differently + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Operand(Some(o)) => { + return Some(o); + } + LocalRef::Operand(None) => { + bug!("use of {:?} before def", place); + } + LocalRef::Place(..) => { + // use path below + } + } + } + + // Moves out of scalar and scalar pair fields are trivial. + if let &mir::Place::Projection(ref proj) = place { + if let Some(o) = self.maybe_codegen_consume_direct(bx, &proj.base) { + match proj.elem { + mir::ProjectionElem::Field(ref f, _) => { + return Some(o.extract_field(bx, f.index())); + } + mir::ProjectionElem::Index(_) | + mir::ProjectionElem::ConstantIndex { .. } => { + // ZSTs don't require any actual memory access. + // FIXME(eddyb) deduplicate this with the identical + // checks in `codegen_consume` and `extract_field`. + let elem = o.layout.field(bx.cx, 0); + if elem.is_zst() { + return Some(OperandRef::new_zst(bx.cx, elem)); + } + } + _ => {} + } + } + } + + None + } + + pub fn codegen_consume(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> OperandRef<'tcx> + { + debug!("codegen_consume(place={:?})", place); + + let ty = self.monomorphized_place_ty(place); + let layout = bx.cx.layout_of(ty); + + // ZSTs don't require any actual memory access. + if layout.is_zst() { + return OperandRef::new_zst(bx.cx, layout); + } + + if let Some(o) = self.maybe_codegen_consume_direct(bx, place) { + return o; + } + + // for most places, to consume them we just load them + // out from their home + self.codegen_place(bx, place).load(bx) + } + + pub fn codegen_operand(&mut self, + bx: &Builder<'a, 'tcx>, + operand: &mir::Operand<'tcx>) + -> OperandRef<'tcx> + { + debug!("codegen_operand(operand={:?})", operand); + + match *operand { + mir::Operand::Copy(ref place) | + mir::Operand::Move(ref place) => { + self.codegen_consume(bx, place) + } + + mir::Operand::Constant(ref constant) => { + let ty = self.monomorphize(&constant.ty); + self.mir_constant_to_const_value(bx, constant) + .and_then(|c| OperandRef::from_const(bx, c, ty)) + .unwrap_or_else(|err| { + match constant.literal { + mir::Literal::Promoted { .. } => { + // don't report errors inside promoteds, just warnings. + }, + mir::Literal::Value { .. } => { + err.report(bx.tcx(), constant.span, "const operand") + }, + } + // We've errored, so we don't have to produce working code. + let layout = bx.cx.layout_of(ty); + PlaceRef::new_sized( + C_null(layout.llvm_type(bx.cx).ptr_to()), + layout, + layout.align, + ).load(bx) + }) + } + } + } +} diff --git a/src/librustc_codegen_llvm/mir/place.rs b/src/librustc_codegen_llvm/mir/place.rs new file mode 100644 index 00000000000..bda8c758750 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/place.rs @@ -0,0 +1,498 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::ty::{self, Ty}; +use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; +use rustc::mir; +use rustc::mir::tcx::PlaceTy; +use rustc_data_structures::indexed_vec::Idx; +use base; +use builder::Builder; +use common::{CodegenCx, C_undef, C_usize, C_u8, C_u32, C_uint, C_null, C_uint_big}; +use consts; +use type_of::LayoutLlvmExt; +use type_::Type; +use value::Value; +use glue; + +use std::ptr; + +use super::{FunctionCx, LocalRef}; +use super::operand::{OperandRef, OperandValue}; + +#[derive(Copy, Clone, Debug)] +pub struct PlaceRef<'tcx> { + /// Pointer to the contents of the place + pub llval: ValueRef, + + /// This place's extra data if it is unsized, or null + pub llextra: ValueRef, + + /// Monomorphized type of this place, including variant information + pub layout: TyLayout<'tcx>, + + /// What alignment we know for this place + pub align: Align, +} + +impl<'a, 'tcx> PlaceRef<'tcx> { + pub fn new_sized(llval: ValueRef, + layout: TyLayout<'tcx>, + align: Align) + -> PlaceRef<'tcx> { + PlaceRef { + llval, + llextra: ptr::null_mut(), + layout, + align + } + } + + pub fn alloca(bx: &Builder<'a, 'tcx>, layout: TyLayout<'tcx>, name: &str) + -> PlaceRef<'tcx> { + debug!("alloca({:?}: {:?})", name, layout); + let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align); + Self::new_sized(tmp, layout, layout.align) + } + + pub fn len(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { + if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { + if self.layout.is_unsized() { + assert!(self.has_extra()); + assert_eq!(count, 0); + self.llextra + } else { + C_usize(cx, count) + } + } else { + bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) + } + } + + pub fn has_extra(&self) -> bool { + !self.llextra.is_null() + } + + pub fn load(&self, bx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { + debug!("PlaceRef::load: {:?}", self); + + assert!(!self.has_extra()); + + if self.layout.is_zst() { + return OperandRef::new_zst(bx.cx, self.layout); + } + + let scalar_load_metadata = |load, scalar: &layout::Scalar| { + let vr = scalar.valid_range.clone(); + match scalar.value { + layout::Int(..) => { + let range = scalar.valid_range_exclusive(bx.cx); + if range.start != range.end { + bx.range_metadata(load, range); + } + } + layout::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { + bx.nonnull_metadata(load); + } + _ => {} + } + }; + + let val = if self.layout.is_llvm_immediate() { + let mut const_llval = ptr::null_mut(); + unsafe { + let global = llvm::LLVMIsAGlobalVariable(self.llval); + if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { + const_llval = llvm::LLVMGetInitializer(global); + } + } + + let llval = if !const_llval.is_null() { + const_llval + } else { + let load = bx.load(self.llval, self.align); + if let layout::Abi::Scalar(ref scalar) = self.layout.abi { + scalar_load_metadata(load, scalar); + } + load + }; + OperandValue::Immediate(base::to_immediate(bx, llval, self.layout)) + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + let load = |i, scalar: &layout::Scalar| { + let mut llptr = bx.struct_gep(self.llval, i as u64); + // Make sure to always load i1 as i8. + if scalar.is_bool() { + llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); + } + let load = bx.load(llptr, self.align); + scalar_load_metadata(load, scalar); + if scalar.is_bool() { + bx.trunc(load, Type::i1(bx.cx)) + } else { + load + } + }; + OperandValue::Pair(load(0, a), load(1, b)) + } else { + OperandValue::Ref(self.llval, self.align) + }; + + OperandRef { val, layout: self.layout } + } + + /// Access a field, at a point when the value's case is known. + pub fn project_field(self, bx: &Builder<'a, 'tcx>, ix: usize) -> PlaceRef<'tcx> { + let cx = bx.cx; + let field = self.layout.field(cx, ix); + let offset = self.layout.fields.offset(ix); + let align = self.align.min(self.layout.align).min(field.align); + + let simple = || { + // Unions and newtypes only use an offset of 0. + let llval = if offset.bytes() == 0 { + self.llval + } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { + // Offsets have to match either first or second field. + assert_eq!(offset, a.value.size(cx).abi_align(b.value.align(cx))); + bx.struct_gep(self.llval, 1) + } else { + bx.struct_gep(self.llval, self.layout.llvm_field_index(ix)) + }; + PlaceRef { + // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. + llval: bx.pointercast(llval, field.llvm_type(cx).ptr_to()), + llextra: if cx.type_has_metadata(field.ty) { + self.llextra + } else { + ptr::null_mut() + }, + layout: field, + align, + } + }; + + // Simple cases, which don't need DST adjustment: + // * no metadata available - just log the case + // * known alignment - sized types, [T], str or a foreign type + // * packed struct - there is no alignment padding + match field.ty.sty { + _ if !self.has_extra() => { + debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", + ix, Value(self.llval)); + return simple(); + } + _ if !field.is_unsized() => return simple(), + ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(), + ty::TyAdt(def, _) => { + if def.repr.packed() { + // FIXME(eddyb) generalize the adjustment when we + // start supporting packing to larger alignments. + assert_eq!(self.layout.align.abi(), 1); + return simple(); + } + } + _ => {} + } + + // We need to get the pointer manually now. + // We do this by casting to a *i8, then offsetting it by the appropriate amount. + // We do this instead of, say, simply adjusting the pointer from the result of a GEP + // because the field may have an arbitrary alignment in the LLVM representation + // anyway. + // + // To demonstrate: + // struct Foo { + // x: u16, + // y: T + // } + // + // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that + // the `y` field has 16-bit alignment. + + let meta = self.llextra; + + let unaligned_offset = C_usize(cx, offset.bytes()); + + // Get the alignment of the field + let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); + + // Bump the unaligned offset up to the appropriate alignment using the + // following expression: + // + // (unaligned offset + (align - 1)) & -align + + // Calculate offset + let align_sub_1 = bx.sub(unsized_align, C_usize(cx, 1u64)); + let offset = bx.and(bx.add(unaligned_offset, align_sub_1), + bx.neg(unsized_align)); + + debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); + + // Cast and adjust pointer + let byte_ptr = bx.pointercast(self.llval, Type::i8p(cx)); + let byte_ptr = bx.gep(byte_ptr, &[offset]); + + // Finally, cast back to the type expected + let ll_fty = field.llvm_type(cx); + debug!("struct_field_ptr: Field type is {:?}", ll_fty); + + PlaceRef { + llval: bx.pointercast(byte_ptr, ll_fty.ptr_to()), + llextra: self.llextra, + layout: field, + align, + } + } + + /// Obtain the actual discriminant of a value. + pub fn codegen_get_discr(self, bx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> ValueRef { + let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx); + if self.layout.abi == layout::Abi::Uninhabited { + return C_undef(cast_to); + } + match self.layout.variants { + layout::Variants::Single { index } => { + let discr_val = self.layout.ty.ty_adt_def().map_or( + index as u128, + |def| def.discriminant_for_variant(bx.cx.tcx, index).val); + return C_uint_big(cast_to, discr_val); + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + + let discr = self.project_field(bx, 0); + let lldiscr = discr.load(bx).immediate(); + match self.layout.variants { + layout::Variants::Single { .. } => bug!(), + layout::Variants::Tagged { ref tag, .. } => { + let signed = match tag.value { + layout::Int(_, signed) => signed, + _ => false + }; + bx.intcast(lldiscr, cast_to, signed) + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + let niche_llty = discr.layout.immediate_llvm_type(bx.cx); + if niche_variants.start() == niche_variants.end() { + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_start == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_start) + }; + bx.select(bx.icmp(llvm::IntEQ, lldiscr, niche_llval), + C_uint(cast_to, *niche_variants.start() as u64), + C_uint(cast_to, dataful_variant as u64)) + } else { + // Rebase from niche values to discriminant values. + let delta = niche_start.wrapping_sub(*niche_variants.start() as u128); + let lldiscr = bx.sub(lldiscr, C_uint_big(niche_llty, delta)); + let lldiscr_max = C_uint(niche_llty, *niche_variants.end() as u64); + bx.select(bx.icmp(llvm::IntULE, lldiscr, lldiscr_max), + bx.intcast(lldiscr, cast_to, false), + C_uint(cast_to, dataful_variant as u64)) + } + } + } + } + + /// Set the discriminant for a new value of the given case of the given + /// representation. + pub fn codegen_set_discr(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) { + if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited { + return; + } + match self.layout.variants { + layout::Variants::Single { index } => { + assert_eq!(index, variant_index); + } + layout::Variants::Tagged { .. } => { + let ptr = self.project_field(bx, 0); + let to = self.layout.ty.ty_adt_def().unwrap() + .discriminant_for_variant(bx.tcx(), variant_index) + .val; + bx.store( + C_uint_big(ptr.layout.llvm_type(bx.cx), to), + ptr.llval, + ptr.align); + } + layout::Variants::NicheFilling { + dataful_variant, + ref niche_variants, + niche_start, + .. + } => { + if variant_index != dataful_variant { + if bx.sess().target.target.arch == "arm" || + bx.sess().target.target.arch == "aarch64" { + // Issue #34427: As workaround for LLVM bug on ARM, + // use memset of 0 before assigning niche value. + let llptr = bx.pointercast(self.llval, Type::i8(bx.cx).ptr_to()); + let fill_byte = C_u8(bx.cx, 0); + let (size, align) = self.layout.size_and_align(); + let size = C_usize(bx.cx, size.bytes()); + let align = C_u32(bx.cx, align.abi() as u32); + base::call_memset(bx, llptr, fill_byte, size, align, false); + } + + let niche = self.project_field(bx, 0); + let niche_llty = niche.layout.immediate_llvm_type(bx.cx); + let niche_value = ((variant_index - *niche_variants.start()) as u128) + .wrapping_add(niche_start); + // FIXME(eddyb) Check the actual primitive type here. + let niche_llval = if niche_value == 0 { + // HACK(eddyb) Using `C_null` as it works on all types. + C_null(niche_llty) + } else { + C_uint_big(niche_llty, niche_value) + }; + OperandValue::Immediate(niche_llval).store(bx, niche); + } + } + } + } + + pub fn project_index(&self, bx: &Builder<'a, 'tcx>, llindex: ValueRef) + -> PlaceRef<'tcx> { + PlaceRef { + llval: bx.inbounds_gep(self.llval, &[C_usize(bx.cx, 0), llindex]), + llextra: ptr::null_mut(), + layout: self.layout.field(bx.cx, 0), + align: self.align + } + } + + pub fn project_downcast(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) + -> PlaceRef<'tcx> { + let mut downcast = *self; + downcast.layout = self.layout.for_variant(bx.cx, variant_index); + + // Cast to the appropriate variant struct type. + let variant_ty = downcast.layout.llvm_type(bx.cx); + downcast.llval = bx.pointercast(downcast.llval, variant_ty.ptr_to()); + + downcast + } + + pub fn storage_live(&self, bx: &Builder<'a, 'tcx>) { + bx.lifetime_start(self.llval, self.layout.size); + } + + pub fn storage_dead(&self, bx: &Builder<'a, 'tcx>) { + bx.lifetime_end(self.llval, self.layout.size); + } +} + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_place(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) + -> PlaceRef<'tcx> { + debug!("codegen_place(place={:?})", place); + + let cx = bx.cx; + let tcx = cx.tcx; + + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Place(place) => { + return place; + } + LocalRef::Operand(..) => { + bug!("using operand local {:?} as place", place); + } + } + } + + let result = match *place { + mir::Place::Local(_) => bug!(), // handled above + mir::Place::Static(box mir::Static { def_id, ty }) => { + let layout = cx.layout_of(self.monomorphize(&ty)); + PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align) + }, + mir::Place::Projection(box mir::Projection { + ref base, + elem: mir::ProjectionElem::Deref + }) => { + // Load the pointer from its location. + self.codegen_consume(bx, base).deref(bx.cx) + } + mir::Place::Projection(ref projection) => { + let cg_base = self.codegen_place(bx, &projection.base); + + match projection.elem { + mir::ProjectionElem::Deref => bug!(), + mir::ProjectionElem::Field(ref field, _) => { + cg_base.project_field(bx, field.index()) + } + mir::ProjectionElem::Index(index) => { + let index = &mir::Operand::Copy(mir::Place::Local(index)); + let index = self.codegen_operand(bx, index); + let llindex = index.immediate(); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: false, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + cg_base.project_index(bx, lloffset) + } + mir::ProjectionElem::ConstantIndex { offset, + from_end: true, + min_length: _ } => { + let lloffset = C_usize(bx.cx, offset as u64); + let lllen = cg_base.len(bx.cx); + let llindex = bx.sub(lllen, lloffset); + cg_base.project_index(bx, llindex) + } + mir::ProjectionElem::Subslice { from, to } => { + let mut subslice = cg_base.project_index(bx, + C_usize(bx.cx, from as u64)); + let projected_ty = PlaceTy::Ty { ty: cg_base.layout.ty } + .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); + subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); + + if subslice.layout.is_unsized() { + assert!(cg_base.has_extra()); + subslice.llextra = bx.sub(cg_base.llextra, + C_usize(bx.cx, (from as u64) + (to as u64))); + } + + // Cast the place pointer type to the new + // array or slice type (*[%_; new_len]). + subslice.llval = bx.pointercast(subslice.llval, + subslice.layout.llvm_type(bx.cx).ptr_to()); + + subslice + } + mir::ProjectionElem::Downcast(_, v) => { + cg_base.project_downcast(bx, v) + } + } + } + }; + debug!("codegen_place(place={:?}) => {:?}", place, result); + result + } + + pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { + let tcx = self.cx.tcx; + let place_ty = place.ty(self.mir, tcx); + self.monomorphize(&place_ty.to_ty(tcx)) + } +} diff --git a/src/librustc_codegen_llvm/mir/rvalue.rs b/src/librustc_codegen_llvm/mir/rvalue.rs new file mode 100644 index 00000000000..d1b949d4f73 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/rvalue.rs @@ -0,0 +1,952 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm::{self, ValueRef}; +use rustc::ty::{self, Ty}; +use rustc::ty::cast::{CastTy, IntTy}; +use rustc::ty::layout::{self, LayoutOf}; +use rustc::mir; +use rustc::middle::lang_items::ExchangeMallocFnLangItem; +use rustc_apfloat::{ieee, Float, Status, Round}; +use std::{u128, i128}; + +use base; +use builder::Builder; +use callee; +use common::{self, val_ty}; +use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_undef, C_null, C_usize, C_uint, C_uint_big}; +use consts; +use monomorphize; +use type_::Type; +use type_of::LayoutLlvmExt; +use value::Value; + +use super::{FunctionCx, LocalRef}; +use super::operand::{OperandRef, OperandValue}; +use super::place::PlaceRef; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_rvalue(&mut self, + bx: Builder<'a, 'tcx>, + dest: PlaceRef<'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> Builder<'a, 'tcx> + { + debug!("codegen_rvalue(dest.llval={:?}, rvalue={:?})", + Value(dest.llval), rvalue); + + match *rvalue { + mir::Rvalue::Use(ref operand) => { + let cg_operand = self.codegen_operand(&bx, operand); + // FIXME: consider not copying constants through stack. (fixable by codegenning + // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) + cg_operand.val.store(&bx, dest); + bx + } + + mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, _) => { + // The destination necessarily contains a fat pointer, so if + // it's a scalar pair, it's a fat pointer or newtype thereof. + if dest.layout.is_llvm_scalar_pair() { + // into-coerce of a thin pointer to a fat pointer - just + // use the operand path. + let (bx, temp) = self.codegen_rvalue_operand(bx, rvalue); + temp.val.store(&bx, dest); + return bx; + } + + // Unsize of a nontrivial struct. I would prefer for + // this to be eliminated by MIR building, but + // `CoerceUnsized` can be passed by a where-clause, + // so the (generic) MIR may not be able to expand it. + let operand = self.codegen_operand(&bx, source); + match operand.val { + OperandValue::Pair(..) | + OperandValue::Immediate(_) => { + // unsize from an immediate structure. We don't + // really need a temporary alloca here, but + // avoiding it would require us to have + // `coerce_unsized_into` use extractvalue to + // index into the struct, and this case isn't + // important enough for it. + debug!("codegen_rvalue: creating ugly alloca"); + let scratch = PlaceRef::alloca(&bx, operand.layout, "__unsize_temp"); + scratch.storage_live(&bx); + operand.val.store(&bx, scratch); + base::coerce_unsized_into(&bx, scratch, dest); + scratch.storage_dead(&bx); + } + OperandValue::Ref(llref, align) => { + let source = PlaceRef::new_sized(llref, operand.layout, align); + base::coerce_unsized_into(&bx, source, dest); + } + } + bx + } + + mir::Rvalue::Repeat(ref elem, count) => { + let cg_elem = self.codegen_operand(&bx, elem); + + // Do not generate the loop for zero-sized elements or empty arrays. + if dest.layout.is_zst() { + return bx; + } + + let start = dest.project_index(&bx, C_usize(bx.cx, 0)).llval; + + if let OperandValue::Immediate(v) = cg_elem.val { + let align = C_i32(bx.cx, dest.align.abi() as i32); + let size = C_usize(bx.cx, dest.layout.size.bytes()); + + // Use llvm.memset.p0i8.* to initialize all zero arrays + if common::is_const_integral(v) && common::const_to_uint(v) == 0 { + let fill = C_u8(bx.cx, 0); + base::call_memset(&bx, start, fill, size, align, false); + return bx; + } + + // Use llvm.memset.p0i8.* to initialize byte arrays + let v = base::from_immediate(&bx, v); + if common::val_ty(v) == Type::i8(bx.cx) { + base::call_memset(&bx, start, v, size, align, false); + return bx; + } + } + + let count = C_usize(bx.cx, count); + let end = dest.project_index(&bx, count).llval; + + let header_bx = bx.build_sibling_block("repeat_loop_header"); + let body_bx = bx.build_sibling_block("repeat_loop_body"); + let next_bx = bx.build_sibling_block("repeat_loop_next"); + + bx.br(header_bx.llbb()); + let current = header_bx.phi(common::val_ty(start), &[start], &[bx.llbb()]); + + let keep_going = header_bx.icmp(llvm::IntNE, current, end); + header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb()); + + cg_elem.val.store(&body_bx, + PlaceRef::new_sized(current, cg_elem.layout, dest.align)); + + let next = body_bx.inbounds_gep(current, &[C_usize(bx.cx, 1)]); + body_bx.br(header_bx.llbb()); + header_bx.add_incoming_to_phi(current, next, body_bx.llbb()); + + next_bx + } + + mir::Rvalue::Aggregate(ref kind, ref operands) => { + let (dest, active_field_index) = match **kind { + mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { + dest.codegen_set_discr(&bx, variant_index); + if adt_def.is_enum() { + (dest.project_downcast(&bx, variant_index), active_field_index) + } else { + (dest, active_field_index) + } + } + _ => (dest, None) + }; + for (i, operand) in operands.iter().enumerate() { + let op = self.codegen_operand(&bx, operand); + // Do not generate stores and GEPis for zero-sized fields. + if !op.layout.is_zst() { + let field_index = active_field_index.unwrap_or(i); + op.val.store(&bx, dest.project_field(&bx, field_index)); + } + } + bx + } + + _ => { + assert!(self.rvalue_creates_operand(rvalue)); + let (bx, temp) = self.codegen_rvalue_operand(bx, rvalue); + temp.val.store(&bx, dest); + bx + } + } + } + + pub fn codegen_rvalue_operand(&mut self, + bx: Builder<'a, 'tcx>, + rvalue: &mir::Rvalue<'tcx>) + -> (Builder<'a, 'tcx>, OperandRef<'tcx>) + { + assert!(self.rvalue_creates_operand(rvalue), "cannot codegen {:?} to operand", rvalue); + + match *rvalue { + mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { + let operand = self.codegen_operand(&bx, source); + debug!("cast operand is {:?}", operand); + let cast = bx.cx.layout_of(self.monomorphize(&mir_cast_ty)); + + let val = match *kind { + mir::CastKind::ReifyFnPointer => { + match operand.layout.ty.sty { + ty::TyFnDef(def_id, substs) => { + if bx.cx.tcx.has_attr(def_id, "rustc_args_required_const") { + bug!("reifying a fn ptr that requires \ + const arguments"); + } + OperandValue::Immediate( + callee::resolve_and_get_fn(bx.cx, def_id, substs)) + } + _ => { + bug!("{} cannot be reified to a fn ptr", operand.layout.ty) + } + } + } + mir::CastKind::ClosureFnPointer => { + match operand.layout.ty.sty { + ty::TyClosure(def_id, substs) => { + let instance = monomorphize::resolve_closure( + bx.cx.tcx, def_id, substs, ty::ClosureKind::FnOnce); + OperandValue::Immediate(callee::get_fn(bx.cx, instance)) + } + _ => { + bug!("{} cannot be cast to a fn ptr", operand.layout.ty) + } + } + } + mir::CastKind::UnsafeFnPointer => { + // this is a no-op at the LLVM level + operand.val + } + mir::CastKind::Unsize => { + assert!(cast.is_llvm_scalar_pair()); + match operand.val { + OperandValue::Pair(lldata, llextra) => { + // unsize from a fat pointer - this is a + // "trait-object-to-supertrait" coercion, for + // example, + // &'a fmt::Debug+Send => &'a fmt::Debug, + + // HACK(eddyb) have to bitcast pointers + // until LLVM removes pointee types. + let lldata = bx.pointercast(lldata, + cast.scalar_pair_element_llvm_type(bx.cx, 0)); + OperandValue::Pair(lldata, llextra) + } + OperandValue::Immediate(lldata) => { + // "standard" unsize + let (lldata, llextra) = base::unsize_thin_ptr(&bx, lldata, + operand.layout.ty, cast.ty); + OperandValue::Pair(lldata, llextra) + } + OperandValue::Ref(..) => { + bug!("by-ref operand {:?} in codegen_rvalue_operand", + operand); + } + } + } + mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => { + if let OperandValue::Pair(data_ptr, meta) = operand.val { + if cast.is_llvm_scalar_pair() { + let data_cast = bx.pointercast(data_ptr, + cast.scalar_pair_element_llvm_type(bx.cx, 0)); + OperandValue::Pair(data_cast, meta) + } else { // cast to thin-ptr + // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and + // pointer-cast of that pointer to desired pointer type. + let llcast_ty = cast.immediate_llvm_type(bx.cx); + let llval = bx.pointercast(data_ptr, llcast_ty); + OperandValue::Immediate(llval) + } + } else { + bug!("Unexpected non-Pair operand") + } + } + mir::CastKind::Misc => { + assert!(cast.is_llvm_immediate()); + let ll_t_out = cast.immediate_llvm_type(bx.cx); + if operand.layout.abi == layout::Abi::Uninhabited { + return (bx, OperandRef { + val: OperandValue::Immediate(C_undef(ll_t_out)), + layout: cast, + }); + } + let r_t_in = CastTy::from_ty(operand.layout.ty) + .expect("bad input type for cast"); + let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); + let ll_t_in = operand.layout.immediate_llvm_type(bx.cx); + match operand.layout.variants { + layout::Variants::Single { index } => { + if let Some(def) = operand.layout.ty.ty_adt_def() { + let discr_val = def + .discriminant_for_variant(bx.cx.tcx, index) + .val; + let discr = C_uint_big(ll_t_out, discr_val); + return (bx, OperandRef { + val: OperandValue::Immediate(discr), + layout: cast, + }); + } + } + layout::Variants::Tagged { .. } | + layout::Variants::NicheFilling { .. } => {}, + } + let llval = operand.immediate(); + + let mut signed = false; + if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { + if let layout::Int(_, s) = scalar.value { + signed = s; + + if scalar.valid_range.end() > scalar.valid_range.start() { + // We want `table[e as usize]` to not + // have bound checks, and this is the most + // convenient place to put the `assume`. + + base::call_assume(&bx, bx.icmp( + llvm::IntULE, + llval, + C_uint_big(ll_t_in, *scalar.valid_range.end()) + )); + } + } + } + + let newval = match (r_t_in, r_t_out) { + (CastTy::Int(_), CastTy::Int(_)) => { + bx.intcast(llval, ll_t_out, signed) + } + (CastTy::Float, CastTy::Float) => { + let srcsz = ll_t_in.float_width(); + let dstsz = ll_t_out.float_width(); + if dstsz > srcsz { + bx.fpext(llval, ll_t_out) + } else if srcsz > dstsz { + bx.fptrunc(llval, ll_t_out) + } else { + llval + } + } + (CastTy::Ptr(_), CastTy::Ptr(_)) | + (CastTy::FnPtr, CastTy::Ptr(_)) | + (CastTy::RPtr(_), CastTy::Ptr(_)) => + bx.pointercast(llval, ll_t_out), + (CastTy::Ptr(_), CastTy::Int(_)) | + (CastTy::FnPtr, CastTy::Int(_)) => + bx.ptrtoint(llval, ll_t_out), + (CastTy::Int(_), CastTy::Ptr(_)) => { + let usize_llval = bx.intcast(llval, bx.cx.isize_ty, signed); + bx.inttoptr(usize_llval, ll_t_out) + } + (CastTy::Int(_), CastTy::Float) => + cast_int_to_float(&bx, signed, llval, ll_t_in, ll_t_out), + (CastTy::Float, CastTy::Int(IntTy::I)) => + cast_float_to_int(&bx, true, llval, ll_t_in, ll_t_out), + (CastTy::Float, CastTy::Int(_)) => + cast_float_to_int(&bx, false, llval, ll_t_in, ll_t_out), + _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty) + }; + OperandValue::Immediate(newval) + } + }; + (bx, OperandRef { + val, + layout: cast + }) + } + + mir::Rvalue::Ref(_, bk, ref place) => { + let cg_place = self.codegen_place(&bx, place); + + let ty = cg_place.layout.ty; + + // Note: places are indirect, so storing the `llval` into the + // destination effectively creates a reference. + let val = if !bx.cx.type_has_metadata(ty) { + OperandValue::Immediate(cg_place.llval) + } else { + OperandValue::Pair(cg_place.llval, cg_place.llextra) + }; + (bx, OperandRef { + val, + layout: self.cx.layout_of(self.cx.tcx.mk_ref( + self.cx.tcx.types.re_erased, + ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() } + )), + }) + } + + mir::Rvalue::Len(ref place) => { + let size = self.evaluate_array_len(&bx, place); + let operand = OperandRef { + val: OperandValue::Immediate(size), + layout: bx.cx.layout_of(bx.tcx().types.usize), + }; + (bx, operand) + } + + mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.codegen_operand(&bx, lhs); + let rhs = self.codegen_operand(&bx, rhs); + let llresult = match (lhs.val, rhs.val) { + (OperandValue::Pair(lhs_addr, lhs_extra), + OperandValue::Pair(rhs_addr, rhs_extra)) => { + self.codegen_fat_ptr_binop(&bx, op, + lhs_addr, lhs_extra, + rhs_addr, rhs_extra, + lhs.layout.ty) + } + + (OperandValue::Immediate(lhs_val), + OperandValue::Immediate(rhs_val)) => { + self.codegen_scalar_binop(&bx, op, lhs_val, rhs_val, lhs.layout.ty) + } + + _ => bug!() + }; + let operand = OperandRef { + val: OperandValue::Immediate(llresult), + layout: bx.cx.layout_of( + op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), + }; + (bx, operand) + } + mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { + let lhs = self.codegen_operand(&bx, lhs); + let rhs = self.codegen_operand(&bx, rhs); + let result = self.codegen_scalar_checked_binop(&bx, op, + lhs.immediate(), rhs.immediate(), + lhs.layout.ty); + let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); + let operand_ty = bx.tcx().intern_tup(&[val_ty, bx.tcx().types.bool]); + let operand = OperandRef { + val: result, + layout: bx.cx.layout_of(operand_ty) + }; + + (bx, operand) + } + + mir::Rvalue::UnaryOp(op, ref operand) => { + let operand = self.codegen_operand(&bx, operand); + let lloperand = operand.immediate(); + let is_float = operand.layout.ty.is_fp(); + let llval = match op { + mir::UnOp::Not => bx.not(lloperand), + mir::UnOp::Neg => if is_float { + bx.fneg(lloperand) + } else { + bx.neg(lloperand) + } + }; + (bx, OperandRef { + val: OperandValue::Immediate(llval), + layout: operand.layout, + }) + } + + mir::Rvalue::Discriminant(ref place) => { + let discr_ty = rvalue.ty(&*self.mir, bx.tcx()); + let discr = self.codegen_place(&bx, place) + .codegen_get_discr(&bx, discr_ty); + (bx, OperandRef { + val: OperandValue::Immediate(discr), + layout: self.cx.layout_of(discr_ty) + }) + } + + mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { + assert!(bx.cx.type_is_sized(ty)); + let val = C_usize(bx.cx, bx.cx.size_of(ty).bytes()); + let tcx = bx.tcx(); + (bx, OperandRef { + val: OperandValue::Immediate(val), + layout: self.cx.layout_of(tcx.types.usize), + }) + } + + mir::Rvalue::NullaryOp(mir::NullOp::Box, content_ty) => { + let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); + let (size, align) = bx.cx.size_and_align_of(content_ty); + let llsize = C_usize(bx.cx, size.bytes()); + let llalign = C_usize(bx.cx, align.abi()); + let box_layout = bx.cx.layout_of(bx.tcx().mk_box(content_ty)); + let llty_ptr = box_layout.llvm_type(bx.cx); + + // Allocate space: + let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) { + Ok(id) => id, + Err(s) => { + bx.sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); + } + }; + let instance = ty::Instance::mono(bx.tcx(), def_id); + let r = callee::get_fn(bx.cx, instance); + let val = bx.pointercast(bx.call(r, &[llsize, llalign], None), llty_ptr); + + let operand = OperandRef { + val: OperandValue::Immediate(val), + layout: box_layout, + }; + (bx, operand) + } + mir::Rvalue::Use(ref operand) => { + let operand = self.codegen_operand(&bx, operand); + (bx, operand) + } + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) => { + // According to `rvalue_creates_operand`, only ZST + // aggregate rvalues are allowed to be operands. + let ty = rvalue.ty(self.mir, self.cx.tcx); + (bx, OperandRef::new_zst(self.cx, + self.cx.layout_of(self.monomorphize(&ty)))) + } + } + } + + fn evaluate_array_len(&mut self, + bx: &Builder<'a, 'tcx>, + place: &mir::Place<'tcx>) -> ValueRef + { + // ZST are passed as operands and require special handling + // because codegen_place() panics if Local is operand. + if let mir::Place::Local(index) = *place { + if let LocalRef::Operand(Some(op)) = self.locals[index] { + if let ty::TyArray(_, n) = op.layout.ty.sty { + let n = n.unwrap_usize(bx.cx.tcx); + return common::C_usize(bx.cx, n); + } + } + } + // use common size calculation for non zero-sized types + let cg_value = self.codegen_place(&bx, place); + return cg_value.len(bx.cx); + } + + pub fn codegen_scalar_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>) -> ValueRef { + let is_float = input_ty.is_fp(); + let is_signed = input_ty.is_signed(); + let is_nil = input_ty.is_nil(); + match op { + mir::BinOp::Add => if is_float { + bx.fadd(lhs, rhs) + } else { + bx.add(lhs, rhs) + }, + mir::BinOp::Sub => if is_float { + bx.fsub(lhs, rhs) + } else { + bx.sub(lhs, rhs) + }, + mir::BinOp::Mul => if is_float { + bx.fmul(lhs, rhs) + } else { + bx.mul(lhs, rhs) + }, + mir::BinOp::Div => if is_float { + bx.fdiv(lhs, rhs) + } else if is_signed { + bx.sdiv(lhs, rhs) + } else { + bx.udiv(lhs, rhs) + }, + mir::BinOp::Rem => if is_float { + bx.frem(lhs, rhs) + } else if is_signed { + bx.srem(lhs, rhs) + } else { + bx.urem(lhs, rhs) + }, + mir::BinOp::BitOr => bx.or(lhs, rhs), + mir::BinOp::BitAnd => bx.and(lhs, rhs), + mir::BinOp::BitXor => bx.xor(lhs, rhs), + mir::BinOp::Offset => bx.inbounds_gep(lhs, &[rhs]), + mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), + mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), + mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { + C_bool(bx.cx, match op { + mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, + mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, + _ => unreachable!() + }) + } else if is_float { + bx.fcmp( + base::bin_op_to_fcmp_predicate(op.to_hir_binop()), + lhs, rhs + ) + } else { + bx.icmp( + base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), + lhs, rhs + ) + } + } + } + + pub fn codegen_fat_ptr_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs_addr: ValueRef, + lhs_extra: ValueRef, + rhs_addr: ValueRef, + rhs_extra: ValueRef, + _input_ty: Ty<'tcx>) + -> ValueRef { + match op { + mir::BinOp::Eq => { + bx.and( + bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), + bx.icmp(llvm::IntEQ, lhs_extra, rhs_extra) + ) + } + mir::BinOp::Ne => { + bx.or( + bx.icmp(llvm::IntNE, lhs_addr, rhs_addr), + bx.icmp(llvm::IntNE, lhs_extra, rhs_extra) + ) + } + mir::BinOp::Le | mir::BinOp::Lt | + mir::BinOp::Ge | mir::BinOp::Gt => { + // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1) + let (op, strict_op) = match op { + mir::BinOp::Lt => (llvm::IntULT, llvm::IntULT), + mir::BinOp::Le => (llvm::IntULE, llvm::IntULT), + mir::BinOp::Gt => (llvm::IntUGT, llvm::IntUGT), + mir::BinOp::Ge => (llvm::IntUGE, llvm::IntUGT), + _ => bug!(), + }; + + bx.or( + bx.icmp(strict_op, lhs_addr, rhs_addr), + bx.and( + bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), + bx.icmp(op, lhs_extra, rhs_extra) + ) + ) + } + _ => { + bug!("unexpected fat ptr binop"); + } + } + } + + pub fn codegen_scalar_checked_binop(&mut self, + bx: &Builder<'a, 'tcx>, + op: mir::BinOp, + lhs: ValueRef, + rhs: ValueRef, + input_ty: Ty<'tcx>) -> OperandValue { + // This case can currently arise only from functions marked + // with #[rustc_inherit_overflow_checks] and inlined from + // another crate (mostly core::num generic/#[inline] fns), + // while the current crate doesn't use overflow checks. + if !bx.cx.check_overflow { + let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); + return OperandValue::Pair(val, C_bool(bx.cx, false)); + } + + let (val, of) = match op { + // These are checked using intrinsics + mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { + let oop = match op { + mir::BinOp::Add => OverflowOp::Add, + mir::BinOp::Sub => OverflowOp::Sub, + mir::BinOp::Mul => OverflowOp::Mul, + _ => unreachable!() + }; + let intrinsic = get_overflow_intrinsic(oop, bx, input_ty); + let res = bx.call(intrinsic, &[lhs, rhs], None); + + (bx.extract_value(res, 0), + bx.extract_value(res, 1)) + } + mir::BinOp::Shl | mir::BinOp::Shr => { + let lhs_llty = val_ty(lhs); + let rhs_llty = val_ty(rhs); + let invert_mask = common::shift_mask_val(&bx, lhs_llty, rhs_llty, true); + let outer_bits = bx.and(rhs, invert_mask); + + let of = bx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); + let val = self.codegen_scalar_binop(bx, op, lhs, rhs, input_ty); + + (val, of) + } + _ => { + bug!("Operator `{:?}` is not a checkable operator", op) + } + }; + + OperandValue::Pair(val, of) + } + + pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { + match *rvalue { + mir::Rvalue::Ref(..) | + mir::Rvalue::Len(..) | + mir::Rvalue::Cast(..) | // (*) + mir::Rvalue::BinaryOp(..) | + mir::Rvalue::CheckedBinaryOp(..) | + mir::Rvalue::UnaryOp(..) | + mir::Rvalue::Discriminant(..) | + mir::Rvalue::NullaryOp(..) | + mir::Rvalue::Use(..) => // (*) + true, + mir::Rvalue::Repeat(..) | + mir::Rvalue::Aggregate(..) => { + let ty = rvalue.ty(self.mir, self.cx.tcx); + let ty = self.monomorphize(&ty); + self.cx.layout_of(ty).is_zst() + } + } + + // (*) this is only true if the type is suitable + } +} + +#[derive(Copy, Clone)] +enum OverflowOp { + Add, Sub, Mul +} + +fn get_overflow_intrinsic(oop: OverflowOp, bx: &Builder, ty: Ty) -> ValueRef { + use syntax::ast::IntTy::*; + use syntax::ast::UintTy::*; + use rustc::ty::{TyInt, TyUint}; + + let tcx = bx.tcx(); + + let new_sty = match ty.sty { + TyInt(Isize) => match &tcx.sess.target.target.target_pointer_width[..] { + "16" => TyInt(I16), + "32" => TyInt(I32), + "64" => TyInt(I64), + _ => panic!("unsupported target word size") + }, + TyUint(Usize) => match &tcx.sess.target.target.target_pointer_width[..] { + "16" => TyUint(U16), + "32" => TyUint(U32), + "64" => TyUint(U64), + _ => panic!("unsupported target word size") + }, + ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(), + _ => panic!("tried to get overflow intrinsic for op applied to non-int type") + }; + + let name = match oop { + OverflowOp::Add => match new_sty { + TyInt(I8) => "llvm.sadd.with.overflow.i8", + TyInt(I16) => "llvm.sadd.with.overflow.i16", + TyInt(I32) => "llvm.sadd.with.overflow.i32", + TyInt(I64) => "llvm.sadd.with.overflow.i64", + TyInt(I128) => "llvm.sadd.with.overflow.i128", + + TyUint(U8) => "llvm.uadd.with.overflow.i8", + TyUint(U16) => "llvm.uadd.with.overflow.i16", + TyUint(U32) => "llvm.uadd.with.overflow.i32", + TyUint(U64) => "llvm.uadd.with.overflow.i64", + TyUint(U128) => "llvm.uadd.with.overflow.i128", + + _ => unreachable!(), + }, + OverflowOp::Sub => match new_sty { + TyInt(I8) => "llvm.ssub.with.overflow.i8", + TyInt(I16) => "llvm.ssub.with.overflow.i16", + TyInt(I32) => "llvm.ssub.with.overflow.i32", + TyInt(I64) => "llvm.ssub.with.overflow.i64", + TyInt(I128) => "llvm.ssub.with.overflow.i128", + + TyUint(U8) => "llvm.usub.with.overflow.i8", + TyUint(U16) => "llvm.usub.with.overflow.i16", + TyUint(U32) => "llvm.usub.with.overflow.i32", + TyUint(U64) => "llvm.usub.with.overflow.i64", + TyUint(U128) => "llvm.usub.with.overflow.i128", + + _ => unreachable!(), + }, + OverflowOp::Mul => match new_sty { + TyInt(I8) => "llvm.smul.with.overflow.i8", + TyInt(I16) => "llvm.smul.with.overflow.i16", + TyInt(I32) => "llvm.smul.with.overflow.i32", + TyInt(I64) => "llvm.smul.with.overflow.i64", + TyInt(I128) => "llvm.smul.with.overflow.i128", + + TyUint(U8) => "llvm.umul.with.overflow.i8", + TyUint(U16) => "llvm.umul.with.overflow.i16", + TyUint(U32) => "llvm.umul.with.overflow.i32", + TyUint(U64) => "llvm.umul.with.overflow.i64", + TyUint(U128) => "llvm.umul.with.overflow.i128", + + _ => unreachable!(), + }, + }; + + bx.cx.get_intrinsic(&name) +} + +fn cast_int_to_float(bx: &Builder, + signed: bool, + x: ValueRef, + int_ty: Type, + float_ty: Type) -> ValueRef { + // Most integer types, even i128, fit into [-f32::MAX, f32::MAX] after rounding. + // It's only u128 -> f32 that can cause overflows (i.e., should yield infinity). + // LLVM's uitofp produces undef in those cases, so we manually check for that case. + let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32; + if is_u128_to_f32 { + // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity, + // and for everything else LLVM's uitofp works just fine. + use rustc_apfloat::ieee::Single; + use rustc_apfloat::Float; + const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1) + << (Single::MAX_EXP - Single::PRECISION as i16); + let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP); + let overflow = bx.icmp(llvm::IntUGE, x, max); + let infinity_bits = C_u32(bx.cx, ieee::Single::INFINITY.to_bits() as u32); + let infinity = consts::bitcast(infinity_bits, float_ty); + bx.select(overflow, infinity, bx.uitofp(x, float_ty)) + } else { + if signed { + bx.sitofp(x, float_ty) + } else { + bx.uitofp(x, float_ty) + } + } +} + +fn cast_float_to_int(bx: &Builder, + signed: bool, + x: ValueRef, + float_ty: Type, + int_ty: Type) -> ValueRef { + let fptosui_result = if signed { + bx.fptosi(x, int_ty) + } else { + bx.fptoui(x, int_ty) + }; + + if !bx.sess().opts.debugging_opts.saturating_float_casts { + return fptosui_result; + } + // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the + // destination integer type after rounding towards zero. This `undef` value can cause UB in + // safe code (see issue #10184), so we implement a saturating conversion on top of it: + // Semantically, the mathematical value of the input is rounded towards zero to the next + // mathematical integer, and then the result is clamped into the range of the destination + // integer type. Positive and negative infinity are mapped to the maximum and minimum value of + // the destination integer type. NaN is mapped to 0. + // + // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to + // a value representable in int_ty. + // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. + // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. + // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly + // representable. Note that this only works if float_ty's exponent range is sufficiently large. + // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 + // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. + // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because + // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). + // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. + fn compute_clamp_bounds(signed: bool, int_ty: Type) -> (u128, u128) { + let rounded_min = F::from_i128_r(int_min(signed, int_ty), Round::TowardZero); + assert_eq!(rounded_min.status, Status::OK); + let rounded_max = F::from_u128_r(int_max(signed, int_ty), Round::TowardZero); + assert!(rounded_max.value.is_finite()); + (rounded_min.value.to_bits(), rounded_max.value.to_bits()) + } + fn int_max(signed: bool, int_ty: Type) -> u128 { + let shift_amount = 128 - int_ty.int_width(); + if signed { + i128::MAX as u128 >> shift_amount + } else { + u128::MAX >> shift_amount + } + } + fn int_min(signed: bool, int_ty: Type) -> i128 { + if signed { + i128::MIN >> (128 - int_ty.int_width()) + } else { + 0 + } + } + let float_bits_to_llval = |bits| { + let bits_llval = match float_ty.float_width() { + 32 => C_u32(bx.cx, bits as u32), + 64 => C_u64(bx.cx, bits as u64), + n => bug!("unsupported float width {}", n), + }; + consts::bitcast(bits_llval, float_ty) + }; + let (f_min, f_max) = match float_ty.float_width() { + 32 => compute_clamp_bounds::(signed, int_ty), + 64 => compute_clamp_bounds::(signed, int_ty), + n => bug!("unsupported float width {}", n), + }; + let f_min = float_bits_to_llval(f_min); + let f_max = float_bits_to_llval(f_max); + // To implement saturation, we perform the following steps: + // + // 1. Cast x to an integer with fpto[su]i. This may result in undef. + // 2. Compare x to f_min and f_max, and use the comparison results to select: + // a) int_ty::MIN if x < f_min or x is NaN + // b) int_ty::MAX if x > f_max + // c) the result of fpto[su]i otherwise + // 3. If x is NaN, return 0.0, otherwise return the result of step 2. + // + // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the + // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of + // undef does not introduce any non-determinism either. + // More importantly, the above procedure correctly implements saturating conversion. + // Proof (sketch): + // If x is NaN, 0 is returned by definition. + // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. + // This yields three cases to consider: + // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with + // saturating conversion for inputs in that range. + // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded + // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger + // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX + // is correct. + // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals + // int_ty::MIN and therefore the return value of int_ty::MIN is correct. + // QED. + + // Step 1 was already performed above. + + // Step 2: We use two comparisons and two selects, with %s1 being the result: + // %less_or_nan = fcmp ult %x, %f_min + // %greater = fcmp olt %x, %f_max + // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result + // %s1 = select %greater, int_ty::MAX, %s0 + // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the + // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 + // becomes int_ty::MIN if x is NaN. + // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a + // negation, and the negation can be merged into the select. Therefore, it not necessarily any + // more expensive than a ordered ("normal") comparison. Whether these optimizations will be + // performed is ultimately up to the backend, but at least x86 does perform them. + let less_or_nan = bx.fcmp(llvm::RealULT, x, f_min); + let greater = bx.fcmp(llvm::RealOGT, x, f_max); + let int_max = C_uint_big(int_ty, int_max(signed, int_ty)); + let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128); + let s0 = bx.select(less_or_nan, int_min, fptosui_result); + let s1 = bx.select(greater, int_max, s0); + + // Step 3: NaN replacement. + // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. + // Therefore we only need to execute this step for signed integer types. + if signed { + // LLVM has no isNaN predicate, so we use (x == x) instead + bx.select(bx.fcmp(llvm::RealOEQ, x, x), s1, C_uint(int_ty, 0)) + } else { + s1 + } +} diff --git a/src/librustc_codegen_llvm/mir/statement.rs b/src/librustc_codegen_llvm/mir/statement.rs new file mode 100644 index 00000000000..578481df157 --- /dev/null +++ b/src/librustc_codegen_llvm/mir/statement.rs @@ -0,0 +1,91 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::mir; + +use asm; +use builder::Builder; + +use super::FunctionCx; +use super::LocalRef; + +impl<'a, 'tcx> FunctionCx<'a, 'tcx> { + pub fn codegen_statement(&mut self, + bx: Builder<'a, 'tcx>, + statement: &mir::Statement<'tcx>) + -> Builder<'a, 'tcx> { + debug!("codegen_statement(statement={:?})", statement); + + self.set_debug_loc(&bx, statement.source_info); + match statement.kind { + mir::StatementKind::Assign(ref place, ref rvalue) => { + if let mir::Place::Local(index) = *place { + match self.locals[index] { + LocalRef::Place(cg_dest) => { + self.codegen_rvalue(bx, cg_dest, rvalue) + } + LocalRef::Operand(None) => { + let (bx, operand) = self.codegen_rvalue_operand(bx, rvalue); + self.locals[index] = LocalRef::Operand(Some(operand)); + bx + } + LocalRef::Operand(Some(op)) => { + if !op.layout.is_zst() { + span_bug!(statement.source_info.span, + "operand {:?} already assigned", + rvalue); + } + + // If the type is zero-sized, it's already been set here, + // but we still need to make sure we codegen the operand + self.codegen_rvalue_operand(bx, rvalue).0 + } + } + } else { + let cg_dest = self.codegen_place(&bx, place); + self.codegen_rvalue(bx, cg_dest, rvalue) + } + } + mir::StatementKind::SetDiscriminant{ref place, variant_index} => { + self.codegen_place(&bx, place) + .codegen_set_discr(&bx, variant_index); + bx + } + mir::StatementKind::StorageLive(local) => { + if let LocalRef::Place(cg_place) = self.locals[local] { + cg_place.storage_live(&bx); + } + bx + } + mir::StatementKind::StorageDead(local) => { + if let LocalRef::Place(cg_place) = self.locals[local] { + cg_place.storage_dead(&bx); + } + bx + } + mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { + let outputs = outputs.iter().map(|output| { + self.codegen_place(&bx, output) + }).collect(); + + let input_vals = inputs.iter().map(|input| { + self.codegen_operand(&bx, input).immediate() + }).collect(); + + asm::codegen_inline_asm(&bx, asm, outputs, input_vals); + bx + } + mir::StatementKind::EndRegion(_) | + mir::StatementKind::Validate(..) | + mir::StatementKind::UserAssertTy(..) | + mir::StatementKind::Nop => bx, + } + } +} diff --git a/src/librustc_codegen_llvm/mono_item.rs b/src/librustc_codegen_llvm/mono_item.rs new file mode 100644 index 00000000000..6ba3582f014 --- /dev/null +++ b/src/librustc_codegen_llvm/mono_item.rs @@ -0,0 +1,193 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use asm; +use attributes; +use base; +use consts; +use context::CodegenCx; +use declare; +use llvm; +use monomorphize::Instance; +use type_of::LayoutLlvmExt; +use rustc::hir; +use rustc::hir::def::Def; +use rustc::hir::def_id::DefId; +use rustc::mir::mono::{Linkage, Visibility}; +use rustc::ty::TypeFoldable; +use rustc::ty::layout::LayoutOf; +use syntax::attr; +use std::fmt; + +pub use rustc::mir::mono::MonoItem; + +pub use rustc_mir::monomorphize::item::*; +pub use rustc_mir::monomorphize::item::MonoItemExt as BaseMonoItemExt; + +pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { + fn define(&self, cx: &CodegenCx<'a, 'tcx>) { + debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + + match *self.as_mono_item() { + MonoItem::Static(def_id) => { + let tcx = cx.tcx; + let is_mutable = match tcx.describe_def(def_id) { + Some(Def::Static(_, is_mutable)) => is_mutable, + Some(other) => { + bug!("Expected Def::Static, found {:?}", other) + } + None => { + bug!("Expected Def::Static for {:?}, found nothing", def_id) + } + }; + let attrs = tcx.get_attrs(def_id); + + consts::codegen_static(&cx, def_id, is_mutable, &attrs); + } + MonoItem::GlobalAsm(node_id) => { + let item = cx.tcx.hir.expect_item(node_id); + if let hir::ItemGlobalAsm(ref ga) = item.node { + asm::codegen_global_asm(cx, ga); + } else { + span_bug!(item.span, "Mismatch between hir::Item type and MonoItem type") + } + } + MonoItem::Fn(instance) => { + base::codegen_instance(&cx, instance); + } + } + + debug!("END IMPLEMENTING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + } + + fn predefine(&self, + cx: &CodegenCx<'a, 'tcx>, + linkage: Linkage, + visibility: Visibility) { + debug!("BEGIN PREDEFINING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + + let symbol_name = self.symbol_name(cx.tcx).as_str(); + + debug!("symbol {}", &symbol_name); + + match *self.as_mono_item() { + MonoItem::Static(def_id) => { + predefine_static(cx, def_id, linkage, visibility, &symbol_name); + } + MonoItem::Fn(instance) => { + predefine_fn(cx, instance, linkage, visibility, &symbol_name); + } + MonoItem::GlobalAsm(..) => {} + } + + debug!("END PREDEFINING '{} ({})' in cgu {}", + self.to_string(cx.tcx), + self.to_raw_string(), + cx.codegen_unit.name()); + } + + fn to_raw_string(&self) -> String { + match *self.as_mono_item() { + MonoItem::Fn(instance) => { + format!("Fn({:?}, {})", + instance.def, + instance.substs.as_ptr() as usize) + } + MonoItem::Static(id) => { + format!("Static({:?})", id) + } + MonoItem::GlobalAsm(id) => { + format!("GlobalAsm({:?})", id) + } + } + } +} + +impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {} + +fn predefine_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + def_id: DefId, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str) { + let instance = Instance::mono(cx.tcx, def_id); + let ty = instance.ty(cx.tcx); + let llty = cx.layout_of(ty).llvm_type(cx); + + let g = declare::define_global(cx, symbol_name, llty).unwrap_or_else(|| { + cx.sess().span_fatal(cx.tcx.def_span(def_id), + &format!("symbol `{}` is already defined", symbol_name)) + }); + + unsafe { + llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage)); + llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility)); + } + + cx.instances.borrow_mut().insert(instance, g); + cx.statics.borrow_mut().insert(g, def_id); +} + +fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + instance: Instance<'tcx>, + linkage: Linkage, + visibility: Visibility, + symbol_name: &str) { + assert!(!instance.substs.needs_infer() && + !instance.substs.has_param_types()); + + let mono_ty = instance.ty(cx.tcx); + let attrs = instance.def.attrs(cx.tcx); + let lldecl = declare::declare_fn(cx, symbol_name, mono_ty); + unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; + base::set_link_section(cx, lldecl, &attrs); + if linkage == Linkage::LinkOnceODR || + linkage == Linkage::WeakODR { + llvm::SetUniqueComdat(cx.llmod, lldecl); + } + + // If we're compiling the compiler-builtins crate, e.g. the equivalent of + // compiler-rt, then we want to implicitly compile everything with hidden + // visibility as we're going to link this object all over the place but + // don't want the symbols to get exported. + if linkage != Linkage::Internal && linkage != Linkage::Private && + attr::contains_name(cx.tcx.hir.krate_attrs(), "compiler_builtins") { + unsafe { + llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden); + } + } else { + unsafe { + llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility)); + } + } + + debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); + if instance.def.is_inline(cx.tcx) { + attributes::inline(lldecl, attributes::InlineAttr::Hint); + } + attributes::from_fn_attrs(cx, lldecl, instance.def.def_id()); + + cx.instances.borrow_mut().insert(instance, lldecl); +} diff --git a/src/librustc_codegen_llvm/time_graph.rs b/src/librustc_codegen_llvm/time_graph.rs new file mode 100644 index 00000000000..a8502682a80 --- /dev/null +++ b/src/librustc_codegen_llvm/time_graph.rs @@ -0,0 +1,278 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::collections::HashMap; +use std::fs::File; +use std::io::prelude::*; +use std::marker::PhantomData; +use std::mem; +use std::sync::{Arc, Mutex}; +use std::time::Instant; + +const OUTPUT_WIDTH_IN_PX: u64 = 1000; +const TIME_LINE_HEIGHT_IN_PX: u64 = 20; +const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30; + +#[derive(Clone)] +struct Timing { + start: Instant, + end: Instant, + work_package_kind: WorkPackageKind, + name: String, + events: Vec<(String, Instant)>, +} + +#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] +pub struct TimelineId(pub usize); + +#[derive(Clone)] +struct PerThread { + timings: Vec, + open_work_package: Option<(Instant, WorkPackageKind, String)>, +} + +#[derive(Clone)] +pub struct TimeGraph { + data: Arc>>, +} + +#[derive(Clone, Copy)] +pub struct WorkPackageKind(pub &'static [&'static str]); + +pub struct Timeline { + token: Option, +} + +struct RaiiToken { + graph: TimeGraph, + timeline: TimelineId, + events: Vec<(String, Instant)>, + // The token must not be Send: + _marker: PhantomData<*const ()> +} + + +impl Drop for RaiiToken { + fn drop(&mut self) { + self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); + } +} + +impl TimeGraph { + pub fn new() -> TimeGraph { + TimeGraph { + data: Arc::new(Mutex::new(HashMap::new())) + } + } + + pub fn start(&self, + timeline: TimelineId, + work_package_kind: WorkPackageKind, + name: &str) -> Timeline { + { + let mut table = self.data.lock().unwrap(); + + let data = table.entry(timeline).or_insert(PerThread { + timings: Vec::new(), + open_work_package: None, + }); + + assert!(data.open_work_package.is_none()); + data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); + } + + Timeline { + token: Some(RaiiToken { + graph: self.clone(), + timeline, + events: Vec::new(), + _marker: PhantomData, + }), + } + } + + fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { + let end = Instant::now(); + + let mut table = self.data.lock().unwrap(); + let data = table.get_mut(&timeline).unwrap(); + + if let Some((start, work_package_kind, name)) = data.open_work_package.take() { + data.timings.push(Timing { + start, + end, + work_package_kind, + name, + events, + }); + } else { + bug!("end timing without start?") + } + } + + pub fn dump(&self, output_filename: &str) { + let table = self.data.lock().unwrap(); + + for data in table.values() { + assert!(data.open_work_package.is_none()); + } + + let mut threads: Vec = + table.values().map(|data| data.clone()).collect(); + + threads.sort_by_key(|timeline| timeline.timings[0].start); + + let earliest_instant = threads[0].timings[0].start; + let latest_instant = threads.iter() + .map(|timeline| timeline.timings + .last() + .unwrap() + .end) + .max() + .unwrap(); + let max_distance = distance(earliest_instant, latest_instant); + + let mut file = File::create(format!("{}.html", output_filename)).unwrap(); + + writeln!(file, " + + + + + +

+ ").unwrap(); + + let mut idx = 0; + for thread in threads.iter() { + for timing in &thread.timings { + let colors = timing.work_package_kind.0; + let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); + writeln!(file, "
", + idx, + colors[idx % colors.len()], + height).unwrap(); + idx += 1; + let max = distance(timing.start, timing.end); + for (i, &(ref event, time)) in timing.events.iter().enumerate() { + let i = i as u64; + let time = distance(timing.start, time); + let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); + writeln!(file, "{}", + at, + TIME_LINE_HEIGHT_IN_PX * i, + event).unwrap(); + } + writeln!(file, "
").unwrap(); + } + } + + writeln!(file, " + + + ").unwrap(); + } +} + +impl Timeline { + pub fn noop() -> Timeline { + Timeline { token: None } + } + + /// Record an event which happened at this moment on this timeline. + /// + /// Events are displayed in the eventual HTML output where you can click on + /// a particular timeline and it'll expand to all of the events that + /// happened on that timeline. This can then be used to drill into a + /// particular timeline and see what events are happening and taking the + /// most time. + pub fn record(&mut self, name: &str) { + if let Some(ref mut token) = self.token { + token.events.push((name.to_string(), Instant::now())); + } + } +} + +fn distance(zero: Instant, x: Instant) -> u64 { + + let duration = x.duration_since(zero); + (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div +} + +fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 { + (max_pixels * distance) / max +} + diff --git a/src/librustc_codegen_llvm/type_.rs b/src/librustc_codegen_llvm/type_.rs new file mode 100644 index 00000000000..a77acc4f175 --- /dev/null +++ b/src/librustc_codegen_llvm/type_.rs @@ -0,0 +1,300 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![allow(non_upper_case_globals)] + +use llvm; +use llvm::{ContextRef, TypeRef, Bool, False, True, TypeKind}; +use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; + +use context::CodegenCx; + +use syntax::ast; +use rustc::ty::layout::{self, Align, Size}; + +use std::ffi::CString; +use std::fmt; +use std::mem; +use std::ptr; + +use libc::c_uint; + +#[derive(Clone, Copy, PartialEq)] +#[repr(C)] +pub struct Type { + rf: TypeRef +} + +impl fmt::Debug for Type { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&llvm::build_string(|s| unsafe { + llvm::LLVMRustWriteTypeToString(self.to_ref(), s); + }).expect("non-UTF8 type description from LLVM")) + } +} + +macro_rules! ty { + ($e:expr) => ( Type::from_ref(unsafe { $e })) +} + +/// Wrapper for LLVM TypeRef +impl Type { + #[inline(always)] + pub fn from_ref(r: TypeRef) -> Type { + Type { + rf: r + } + } + + #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler + pub fn to_ref(&self) -> TypeRef { + self.rf + } + + pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] { + unsafe { mem::transmute(slice) } + } + + pub fn void(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMVoidTypeInContext(cx.llcx)) + } + + pub fn metadata(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMRustMetadataTypeInContext(cx.llcx)) + } + + pub fn i1(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt1TypeInContext(cx.llcx)) + } + + pub fn i8(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt8TypeInContext(cx.llcx)) + } + + pub fn i8_llcx(llcx: ContextRef) -> Type { + ty!(llvm::LLVMInt8TypeInContext(llcx)) + } + + pub fn i16(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt16TypeInContext(cx.llcx)) + } + + pub fn i32(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt32TypeInContext(cx.llcx)) + } + + pub fn i64(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMInt64TypeInContext(cx.llcx)) + } + + pub fn i128(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMIntTypeInContext(cx.llcx, 128)) + } + + // Creates an integer type with the given number of bits, e.g. i24 + pub fn ix(cx: &CodegenCx, num_bits: u64) -> Type { + ty!(llvm::LLVMIntTypeInContext(cx.llcx, num_bits as c_uint)) + } + + pub fn f32(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMFloatTypeInContext(cx.llcx)) + } + + pub fn f64(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMDoubleTypeInContext(cx.llcx)) + } + + pub fn bool(cx: &CodegenCx) -> Type { + Type::i8(cx) + } + + pub fn char(cx: &CodegenCx) -> Type { + Type::i32(cx) + } + + pub fn i8p(cx: &CodegenCx) -> Type { + Type::i8(cx).ptr_to() + } + + pub fn i8p_llcx(llcx: ContextRef) -> Type { + Type::i8_llcx(llcx).ptr_to() + } + + pub fn isize(cx: &CodegenCx) -> Type { + match &cx.tcx.sess.target.target.target_pointer_width[..] { + "16" => Type::i16(cx), + "32" => Type::i32(cx), + "64" => Type::i64(cx), + tws => bug!("Unsupported target word size for int: {}", tws), + } + } + + pub fn c_int(cx: &CodegenCx) -> Type { + match &cx.tcx.sess.target.target.target_c_int_width[..] { + "16" => Type::i16(cx), + "32" => Type::i32(cx), + "64" => Type::i64(cx), + width => bug!("Unsupported target_c_int_width: {}", width), + } + } + + pub fn int_from_ty(cx: &CodegenCx, t: ast::IntTy) -> Type { + match t { + ast::IntTy::Isize => cx.isize_ty, + ast::IntTy::I8 => Type::i8(cx), + ast::IntTy::I16 => Type::i16(cx), + ast::IntTy::I32 => Type::i32(cx), + ast::IntTy::I64 => Type::i64(cx), + ast::IntTy::I128 => Type::i128(cx), + } + } + + pub fn uint_from_ty(cx: &CodegenCx, t: ast::UintTy) -> Type { + match t { + ast::UintTy::Usize => cx.isize_ty, + ast::UintTy::U8 => Type::i8(cx), + ast::UintTy::U16 => Type::i16(cx), + ast::UintTy::U32 => Type::i32(cx), + ast::UintTy::U64 => Type::i64(cx), + ast::UintTy::U128 => Type::i128(cx), + } + } + + pub fn float_from_ty(cx: &CodegenCx, t: ast::FloatTy) -> Type { + match t { + ast::FloatTy::F32 => Type::f32(cx), + ast::FloatTy::F64 => Type::f64(cx), + } + } + + pub fn func(args: &[Type], ret: &Type) -> Type { + let slice: &[TypeRef] = Type::to_ref_slice(args); + ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), + args.len() as c_uint, False)) + } + + pub fn variadic_func(args: &[Type], ret: &Type) -> Type { + let slice: &[TypeRef] = Type::to_ref_slice(args); + ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), + args.len() as c_uint, True)) + } + + pub fn struct_(cx: &CodegenCx, els: &[Type], packed: bool) -> Type { + let els: &[TypeRef] = Type::to_ref_slice(els); + ty!(llvm::LLVMStructTypeInContext(cx.llcx, els.as_ptr(), + els.len() as c_uint, + packed as Bool)) + } + + pub fn named_struct(cx: &CodegenCx, name: &str) -> Type { + let name = CString::new(name).unwrap(); + ty!(llvm::LLVMStructCreateNamed(cx.llcx, name.as_ptr())) + } + + + pub fn array(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) + } + + pub fn vector(ty: &Type, len: u64) -> Type { + ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) + } + + pub fn kind(&self) -> TypeKind { + unsafe { + llvm::LLVMRustGetTypeKind(self.to_ref()) + } + } + + pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { + let slice: &[TypeRef] = Type::to_ref_slice(els); + unsafe { + llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(), + els.len() as c_uint, packed as Bool) + } + } + + pub fn ptr_to(&self) -> Type { + ty!(llvm::LLVMPointerType(self.to_ref(), 0)) + } + + pub fn element_type(&self) -> Type { + unsafe { + Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) + } + } + + /// Return the number of elements in `self` if it is a LLVM vector type. + pub fn vector_length(&self) -> usize { + unsafe { + llvm::LLVMGetVectorSize(self.to_ref()) as usize + } + } + + pub fn func_params(&self) -> Vec { + unsafe { + let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize; + let mut args = vec![Type { rf: ptr::null_mut() }; n_args]; + llvm::LLVMGetParamTypes(self.to_ref(), + args.as_mut_ptr() as *mut TypeRef); + args + } + } + + pub fn float_width(&self) -> usize { + match self.kind() { + Float => 32, + Double => 64, + X86_FP80 => 80, + FP128 | PPC_FP128 => 128, + _ => bug!("llvm_float_width called on a non-float type") + } + } + + /// Retrieve the bit width of the integer type `self`. + pub fn int_width(&self) -> u64 { + unsafe { + llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64 + } + } + + pub fn from_integer(cx: &CodegenCx, i: layout::Integer) -> Type { + use rustc::ty::layout::Integer::*; + match i { + I8 => Type::i8(cx), + I16 => Type::i16(cx), + I32 => Type::i32(cx), + I64 => Type::i64(cx), + I128 => Type::i128(cx), + } + } + + /// Return a LLVM type that has at most the required alignment, + /// as a conservative approximation for unknown pointee types. + pub fn pointee_for_abi_align(cx: &CodegenCx, align: Align) -> Type { + // FIXME(eddyb) We could find a better approximation if ity.align < align. + let ity = layout::Integer::approximate_abi_align(cx, align); + Type::from_integer(cx, ity) + } + + /// Return a LLVM type that has at most the required alignment, + /// and exactly the required size, as a best-effort padding array. + pub fn padding_filler(cx: &CodegenCx, size: Size, align: Align) -> Type { + let unit = layout::Integer::approximate_abi_align(cx, align); + let size = size.bytes(); + let unit_size = unit.size().bytes(); + assert_eq!(size % unit_size, 0); + Type::array(&Type::from_integer(cx, unit), size / unit_size) + } + + pub fn x86_mmx(cx: &CodegenCx) -> Type { + ty!(llvm::LLVMX86MMXTypeInContext(cx.llcx)) + } +} diff --git a/src/librustc_codegen_llvm/type_of.rs b/src/librustc_codegen_llvm/type_of.rs new file mode 100644 index 00000000000..5f186e7514e --- /dev/null +++ b/src/librustc_codegen_llvm/type_of.rs @@ -0,0 +1,506 @@ +// Copyright 2012-2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use abi::{FnType, FnTypeExt}; +use common::*; +use rustc::hir; +use rustc::ty::{self, Ty, TypeFoldable}; +use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; +use rustc_target::spec::PanicStrategy; +use mono_item::DefPathBasedNames; +use type_::Type; + +use std::fmt::Write; + +fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>, + defer: &mut Option<(Type, TyLayout<'tcx>)>) + -> Type { + match layout.abi { + layout::Abi::Scalar(_) => bug!("handled elsewhere"), + layout::Abi::Vector { ref element, count } => { + // LLVM has a separate type for 64-bit SIMD vectors on X86 called + // `x86_mmx` which is needed for some SIMD operations. As a bit of a + // hack (all SIMD definitions are super unstable anyway) we + // recognize any one-element SIMD vector as "this should be an + // x86_mmx" type. In general there shouldn't be a need for other + // one-element SIMD vectors, so it's assumed this won't clash with + // much else. + let use_x86_mmx = count == 1 && layout.size.bits() == 64 && + (cx.sess().target.target.arch == "x86" || + cx.sess().target.target.arch == "x86_64"); + if use_x86_mmx { + return Type::x86_mmx(cx) + } else { + let element = layout.scalar_llvm_type_at(cx, element, Size::from_bytes(0)); + return Type::vector(&element, count); + } + } + layout::Abi::ScalarPair(..) => { + return Type::struct_(cx, &[ + layout.scalar_pair_element_llvm_type(cx, 0), + layout.scalar_pair_element_llvm_type(cx, 1), + ], false); + } + layout::Abi::Uninhabited | + layout::Abi::Aggregate { .. } => {} + } + + let name = match layout.ty.sty { + ty::TyClosure(..) | + ty::TyGenerator(..) | + ty::TyAdt(..) | + // FIXME(eddyb) producing readable type names for trait objects can result + // in problematically distinct types due to HRTB and subtyping (see #47638). + // ty::TyDynamic(..) | + ty::TyForeign(..) | + ty::TyStr => { + let mut name = String::with_capacity(32); + let printer = DefPathBasedNames::new(cx.tcx, true, true); + printer.push_type_name(layout.ty, &mut name); + match (&layout.ty.sty, &layout.variants) { + (&ty::TyAdt(def, _), &layout::Variants::Single { index }) => { + if def.is_enum() && !def.variants.is_empty() { + write!(&mut name, "::{}", def.variants[index].name).unwrap(); + } + } + _ => {} + } + Some(name) + } + _ => None + }; + + match layout.fields { + layout::FieldPlacement::Union(_) => { + let fill = Type::padding_filler(cx, layout.size, layout.align); + let packed = false; + match name { + None => { + Type::struct_(cx, &[fill], packed) + } + Some(ref name) => { + let mut llty = Type::named_struct(cx, name); + llty.set_struct_body(&[fill], packed); + llty + } + } + } + layout::FieldPlacement::Array { count, .. } => { + Type::array(&layout.field(cx, 0).llvm_type(cx), count) + } + layout::FieldPlacement::Arbitrary { .. } => { + match name { + None => { + let (llfields, packed) = struct_llfields(cx, layout); + Type::struct_(cx, &llfields, packed) + } + Some(ref name) => { + let llty = Type::named_struct(cx, name); + *defer = Some((llty, layout)); + llty + } + } + } + } +} + +fn struct_llfields<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, + layout: TyLayout<'tcx>) + -> (Vec, bool) { + debug!("struct_llfields: {:#?}", layout); + let field_count = layout.fields.count(); + + let mut packed = false; + let mut offset = Size::from_bytes(0); + let mut prev_align = layout.align; + let mut result: Vec = Vec::with_capacity(1 + field_count * 2); + for i in layout.fields.index_by_increasing_offset() { + let field = layout.field(cx, i); + packed |= layout.align.abi() < field.align.abi(); + + let target_offset = layout.fields.offset(i as usize); + debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", + i, field, offset, target_offset); + assert!(target_offset >= offset); + let padding = target_offset - offset; + let padding_align = layout.align.min(prev_align).min(field.align); + assert_eq!(offset.abi_align(padding_align) + padding, target_offset); + result.push(Type::padding_filler(cx, padding, padding_align)); + debug!(" padding before: {:?}", padding); + + result.push(field.llvm_type(cx)); + offset = target_offset + field.size; + prev_align = field.align; + } + if !layout.is_unsized() && field_count > 0 { + if offset > layout.size { + bug!("layout: {:#?} stride: {:?} offset: {:?}", + layout, layout.size, offset); + } + let padding = layout.size - offset; + let padding_align = layout.align.min(prev_align); + assert_eq!(offset.abi_align(padding_align) + padding, layout.size); + debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", + padding, offset, layout.size); + result.push(Type::padding_filler(cx, padding, padding_align)); + assert!(result.len() == 1 + field_count * 2); + } else { + debug!("struct_llfields: offset: {:?} stride: {:?}", + offset, layout.size); + } + + (result, packed) +} + +impl<'a, 'tcx> CodegenCx<'a, 'tcx> { + pub fn align_of(&self, ty: Ty<'tcx>) -> Align { + self.layout_of(ty).align + } + + pub fn size_of(&self, ty: Ty<'tcx>) -> Size { + self.layout_of(ty).size + } + + pub fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { + self.layout_of(ty).size_and_align() + } +} + +#[derive(Copy, Clone, PartialEq, Eq)] +pub enum PointerKind { + /// Most general case, we know no restrictions to tell LLVM. + Shared, + + /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. + Frozen, + + /// `&mut T`, when we know `noalias` is safe for LLVM. + UniqueBorrowed, + + /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. + UniqueOwned +} + +#[derive(Copy, Clone)] +pub struct PointeeInfo { + pub size: Size, + pub align: Align, + pub safe: Option, +} + +pub trait LayoutLlvmExt<'tcx> { + fn is_llvm_immediate(&self) -> bool; + fn is_llvm_scalar_pair<'a>(&self) -> bool; + fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + scalar: &layout::Scalar, offset: Size) -> Type; + fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + index: usize) -> Type; + fn llvm_field_index(&self, index: usize) -> u64; + fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) + -> Option; +} + +impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { + fn is_llvm_immediate(&self) -> bool { + match self.abi { + layout::Abi::Scalar(_) | + layout::Abi::Vector { .. } => true, + layout::Abi::ScalarPair(..) => false, + layout::Abi::Uninhabited | + layout::Abi::Aggregate { .. } => self.is_zst() + } + } + + fn is_llvm_scalar_pair<'a>(&self) -> bool { + match self.abi { + layout::Abi::ScalarPair(..) => true, + layout::Abi::Uninhabited | + layout::Abi::Scalar(_) | + layout::Abi::Vector { .. } | + layout::Abi::Aggregate { .. } => false + } + } + + /// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. + /// The pointee type of the pointer in `PlaceRef` is always this type. + /// For sized types, it is also the right LLVM type for an `alloca` + /// containing a value of that type, and most immediates (except `bool`). + /// Unsized types, however, are represented by a "minimal unit", e.g. + /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this + /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. + /// If the type is an unsized struct, the regular layout is generated, + /// with the inner-most trailing unsized field using the "minimal unit" + /// of that field's type - this is useful for taking the address of + /// that field and ensuring the struct has the right alignment. + fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + // Use a different cache for scalars because pointers to DSTs + // can be either fat or thin (data pointers of fat pointers). + if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { + return llty; + } + let llty = match self.ty.sty { + ty::TyRef(_, ty, _) | + ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { + cx.layout_of(ty).llvm_type(cx).ptr_to() + } + ty::TyAdt(def, _) if def.is_box() => { + cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to() + } + ty::TyFnPtr(sig) => { + let sig = cx.tcx.normalize_erasing_late_bound_regions( + ty::ParamEnv::reveal_all(), + &sig, + ); + FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() + } + _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0)) + }; + cx.scalar_lltypes.borrow_mut().insert(self.ty, llty); + return llty; + } + + + // Check the cache. + let variant_index = match self.variants { + layout::Variants::Single { index } => Some(index), + _ => None + }; + if let Some(&llty) = cx.lltypes.borrow().get(&(self.ty, variant_index)) { + return llty; + } + + debug!("llvm_type({:#?})", self); + + assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); + + // Make sure lifetimes are erased, to avoid generating distinct LLVM + // types for Rust types that only differ in the choice of lifetimes. + let normal_ty = cx.tcx.erase_regions(&self.ty); + + let mut defer = None; + let llty = if self.ty != normal_ty { + let mut layout = cx.layout_of(normal_ty); + if let Some(v) = variant_index { + layout = layout.for_variant(cx, v); + } + layout.llvm_type(cx) + } else { + uncached_llvm_type(cx, *self, &mut defer) + }; + debug!("--> mapped {:#?} to llty={:?}", self, llty); + + cx.lltypes.borrow_mut().insert((self.ty, variant_index), llty); + + if let Some((mut llty, layout)) = defer { + let (llfields, packed) = struct_llfields(cx, layout); + llty.set_struct_body(&llfields, packed) + } + + llty + } + + fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { + if let layout::Abi::Scalar(ref scalar) = self.abi { + if scalar.is_bool() { + return Type::i1(cx); + } + } + self.llvm_type(cx) + } + + fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + scalar: &layout::Scalar, offset: Size) -> Type { + match scalar.value { + layout::Int(i, _) => Type::from_integer(cx, i), + layout::F32 => Type::f32(cx), + layout::F64 => Type::f64(cx), + layout::Pointer => { + // If we know the alignment, pick something better than i8. + let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { + Type::pointee_for_abi_align(cx, pointee.align) + } else { + Type::i8(cx) + }; + pointee.ptr_to() + } + } + } + + fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, + index: usize) -> Type { + // HACK(eddyb) special-case fat pointers until LLVM removes + // pointee types, to avoid bitcasting every `OperandRef::deref`. + match self.ty.sty { + ty::TyRef(..) | + ty::TyRawPtr(_) => { + return self.field(cx, index).llvm_type(cx); + } + ty::TyAdt(def, _) if def.is_box() => { + let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); + return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index); + } + _ => {} + } + + let (a, b) = match self.abi { + layout::Abi::ScalarPair(ref a, ref b) => (a, b), + _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self) + }; + let scalar = [a, b][index]; + + // Make sure to return the same type `immediate_llvm_type` would, + // to avoid dealing with two types and the associated conversions. + // This means that `(bool, bool)` is represented as `{i1, i1}`, + // both in memory and as an immediate, while `bool` is typically + // `i8` in memory and only `i1` when immediate. While we need to + // load/store `bool` as `i8` to avoid crippling LLVM optimizations, + // `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`. + if scalar.is_bool() { + return Type::i1(cx); + } + + let offset = if index == 0 { + Size::from_bytes(0) + } else { + a.value.size(cx).abi_align(b.value.align(cx)) + }; + self.scalar_llvm_type_at(cx, scalar, offset) + } + + fn llvm_field_index(&self, index: usize) -> u64 { + match self.abi { + layout::Abi::Scalar(_) | + layout::Abi::ScalarPair(..) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + _ => {} + } + match self.fields { + layout::FieldPlacement::Union(_) => { + bug!("TyLayout::llvm_field_index({:?}): not applicable", self) + } + + layout::FieldPlacement::Array { .. } => { + index as u64 + } + + layout::FieldPlacement::Arbitrary { .. } => { + 1 + (self.fields.memory_index(index) as u64) * 2 + } + } + } + + fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) + -> Option { + if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { + return pointee; + } + + let mut result = None; + match self.ty.sty { + ty::TyRawPtr(mt) if offset.bytes() == 0 => { + let (size, align) = cx.size_and_align_of(mt.ty); + result = Some(PointeeInfo { + size, + align, + safe: None + }); + } + + ty::TyRef(_, ty, mt) if offset.bytes() == 0 => { + let (size, align) = cx.size_and_align_of(ty); + + let kind = match mt { + hir::MutImmutable => if cx.type_is_freeze(ty) { + PointerKind::Frozen + } else { + PointerKind::Shared + }, + hir::MutMutable => { + if cx.tcx.sess.opts.debugging_opts.mutable_noalias || + cx.tcx.sess.panic_strategy() == PanicStrategy::Abort { + PointerKind::UniqueBorrowed + } else { + PointerKind::Shared + } + } + }; + + result = Some(PointeeInfo { + size, + align, + safe: Some(kind) + }); + } + + _ => { + let mut data_variant = match self.variants { + layout::Variants::NicheFilling { dataful_variant, .. } => { + // Only the niche itself is always initialized, + // so only check for a pointer at its offset. + // + // If the niche is a pointer, it's either valid + // (according to its type), or null (which the + // niche field's scalar validity range encodes). + // This allows using `dereferenceable_or_null` + // for e.g. `Option<&T>`, and this will continue + // to work as long as we don't start using more + // niches than just null (e.g. the first page + // of the address space, or unaligned pointers). + if self.fields.offset(0) == offset { + Some(self.for_variant(cx, dataful_variant)) + } else { + None + } + } + _ => Some(*self) + }; + + if let Some(variant) = data_variant { + // We're not interested in any unions. + if let layout::FieldPlacement::Union(_) = variant.fields { + data_variant = None; + } + } + + if let Some(variant) = data_variant { + let ptr_end = offset + layout::Pointer.size(cx); + for i in 0..variant.fields.count() { + let field_start = variant.fields.offset(i); + if field_start <= offset { + let field = variant.field(cx, i); + if ptr_end <= field_start + field.size { + // We found the right field, look inside it. + result = field.pointee_info_at(cx, offset - field_start); + break; + } + } + } + } + + // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. + if let Some(ref mut pointee) = result { + if let ty::TyAdt(def, _) = self.ty.sty { + if def.is_box() && offset.bytes() == 0 { + pointee.safe = Some(PointerKind::UniqueOwned); + } + } + } + } + } + + cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); + result + } +} diff --git a/src/librustc_codegen_llvm/value.rs b/src/librustc_codegen_llvm/value.rs new file mode 100644 index 00000000000..287ad87caac --- /dev/null +++ b/src/librustc_codegen_llvm/value.rs @@ -0,0 +1,24 @@ +// Copyright 2013 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use llvm; + +use std::fmt; + +#[derive(Copy, Clone, PartialEq)] +pub struct Value(pub llvm::ValueRef); + +impl fmt::Debug for Value { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.write_str(&llvm::build_string(|s| unsafe { + llvm::LLVMRustWriteValueToString(self.0, s); + }).expect("nun-UTF8 value description from LLVM")) + } +} diff --git a/src/librustc_codegen_utils/Cargo.toml b/src/librustc_codegen_utils/Cargo.toml new file mode 100644 index 00000000000..690fb260390 --- /dev/null +++ b/src/librustc_codegen_utils/Cargo.toml @@ -0,0 +1,23 @@ +[package] +authors = ["The Rust Project Developers"] +name = "rustc_codegen_utils" +version = "0.0.0" + +[lib] +name = "rustc_codegen_utils" +path = "lib.rs" +crate-type = ["dylib"] +test = false + +[dependencies] +ar = "0.3.0" +flate2 = "1.0" +log = "0.4" + +syntax = { path = "../libsyntax" } +syntax_pos = { path = "../libsyntax_pos" } +rustc = { path = "../librustc" } +rustc_target = { path = "../librustc_target" } +rustc_data_structures = { path = "../librustc_data_structures" } +rustc_mir = { path = "../librustc_mir" } +rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_codegen_utils/codegen_backend.rs b/src/librustc_codegen_utils/codegen_backend.rs new file mode 100644 index 00000000000..15aab680289 --- /dev/null +++ b/src/librustc_codegen_utils/codegen_backend.rs @@ -0,0 +1,296 @@ +// Copyright 2014-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust compiler. +//! +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] +#![deny(warnings)] + +#![feature(box_syntax)] + +use std::any::Any; +use std::io::prelude::*; +use std::io::{self, Cursor}; +use std::fs::File; +use std::path::Path; +use std::sync::mpsc; + +use rustc_data_structures::owning_ref::OwningRef; +use rustc_data_structures::sync::Lrc; +use ar::{Archive, Builder, Header}; +use flate2::Compression; +use flate2::write::DeflateEncoder; + +use syntax::symbol::Symbol; +use rustc::hir::def_id::LOCAL_CRATE; +use rustc::session::{Session, CompileIncomplete}; +use rustc::session::config::{CrateType, OutputFilenames, PrintRequest}; +use rustc::ty::TyCtxt; +use rustc::ty::maps::Providers; +use rustc::middle::cstore::EncodedMetadata; +use rustc::middle::cstore::MetadataLoader; +use rustc::dep_graph::DepGraph; +use rustc_target::spec::Target; +use rustc_data_structures::fx::FxHashMap; +use rustc_mir::monomorphize::collector; +use link::{build_link_meta, out_filename}; + +pub use rustc_data_structures::sync::MetadataRef; + +pub trait CodegenBackend { + fn init(&self, _sess: &Session) {} + fn print(&self, _req: PrintRequest, _sess: &Session) {} + fn target_features(&self, _sess: &Session) -> Vec { vec![] } + fn print_passes(&self) {} + fn print_version(&self) {} + fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } + + fn metadata_loader(&self) -> Box; + fn provide(&self, _providers: &mut Providers); + fn provide_extern(&self, _providers: &mut Providers); + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + rx: mpsc::Receiver> + ) -> Box; + + /// This is called on the returned `Box` from `codegen_backend` + /// + /// # Panics + /// + /// Panics when the passed `Box` was not returned by `codegen_backend`. + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete>; +} + +pub struct DummyCodegenBackend; + +impl CodegenBackend for DummyCodegenBackend { + fn metadata_loader(&self) -> Box { + box DummyMetadataLoader(()) + } + + fn provide(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide"); + } + + fn provide_extern(&self, _providers: &mut Providers) { + bug!("DummyCodegenBackend::provide_extern"); + } + + fn codegen_crate<'a, 'tcx>( + &self, + _tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Box { + bug!("DummyCodegenBackend::codegen_backend"); + } + + fn join_codegen_and_link( + &self, + _ongoing_codegen: Box, + _sess: &Session, + _dep_graph: &DepGraph, + _outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + bug!("DummyCodegenBackend::join_codegen_and_link"); + } +} + +pub struct DummyMetadataLoader(()); + +impl MetadataLoader for DummyMetadataLoader { + fn get_rlib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result { + bug!("DummyMetadataLoader::get_rlib_metadata"); + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path + ) -> Result { + bug!("DummyMetadataLoader::get_dylib_metadata"); + } +} + +pub struct NoLlvmMetadataLoader; + +impl MetadataLoader for NoLlvmMetadataLoader { + fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { + let file = File::open(filename) + .map_err(|e| format!("metadata file open err: {:?}", e))?; + let mut archive = Archive::new(file); + + while let Some(entry_result) = archive.next_entry() { + let mut entry = entry_result + .map_err(|e| format!("metadata section read err: {:?}", e))?; + if entry.header().identifier() == "rust.metadata.bin" { + let mut buf = Vec::new(); + io::copy(&mut entry, &mut buf).unwrap(); + let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); + return Ok(rustc_erase_owner!(buf.map_owner_box())); + } + } + + Err("Couldn't find metadata section".to_string()) + } + + fn get_dylib_metadata( + &self, + _target: &Target, + _filename: &Path, + ) -> Result { + // FIXME: Support reading dylibs from llvm enabled rustc + self.get_rlib_metadata(_target, _filename) + } +} + +pub struct MetadataOnlyCodegenBackend(()); +pub struct OngoingCodegen { + metadata: EncodedMetadata, + metadata_version: Vec, + crate_name: Symbol, +} + +impl MetadataOnlyCodegenBackend { + pub fn new() -> Box { + box MetadataOnlyCodegenBackend(()) + } +} + +impl CodegenBackend for MetadataOnlyCodegenBackend { + fn init(&self, sess: &Session) { + for cty in sess.opts.crate_types.iter() { + match *cty { + CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | + CrateType::CrateTypeExecutable => {}, + _ => { + sess.parse_sess.span_diagnostic.warn( + &format!("LLVM unsupported, so output type {} is not supported", cty) + ); + }, + } + } + } + + fn metadata_loader(&self) -> Box { + box NoLlvmMetadataLoader + } + + fn provide(&self, providers: &mut Providers) { + ::symbol_names::provide(providers); + + providers.target_features_whitelist = |_tcx, _cnum| { + Lrc::new(FxHashMap()) // Just a dummy + }; + } + fn provide_extern(&self, _providers: &mut Providers) {} + + fn codegen_crate<'a, 'tcx>( + &self, + tcx: TyCtxt<'a, 'tcx, 'tcx>, + _rx: mpsc::Receiver> + ) -> Box { + use rustc_mir::monomorphize::item::MonoItem; + + ::check_for_rustc_errors_attr(tcx); + ::symbol_names_test::report_symbol_names(tcx); + ::rustc_incremental::assert_dep_graph(tcx); + ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); + ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0.iter() + ); + ::rustc::middle::dependency_format::calculate(tcx); + let _ = tcx.link_args(LOCAL_CRATE); + let _ = tcx.native_libraries(LOCAL_CRATE); + for mono_item in + collector::collect_crate_mono_items( + tcx, + collector::MonoItemCollectionMode::Eager + ).0 { + match mono_item { + MonoItem::Fn(inst) => { + let def_id = inst.def_id(); + if def_id.is_local() { + let _ = inst.def.is_inline(tcx); + let _ = tcx.codegen_fn_attrs(def_id); + } + } + _ => {} + } + } + tcx.sess.abort_if_errors(); + + let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE)); + let metadata = tcx.encode_metadata(&link_meta); + + box OngoingCodegen { + metadata: metadata, + metadata_version: tcx.metadata_encoding_version().to_vec(), + crate_name: tcx.crate_name(LOCAL_CRATE), + } + } + + fn join_codegen_and_link( + &self, + ongoing_codegen: Box, + sess: &Session, + _dep_graph: &DepGraph, + outputs: &OutputFilenames, + ) -> Result<(), CompileIncomplete> { + let ongoing_codegen = ongoing_codegen.downcast::() + .expect("Expected MetadataOnlyCodegenBackend's OngoingCodegen, found Box"); + for &crate_type in sess.opts.crate_types.iter() { + if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { + continue; + } + let output_name = + out_filename(sess, crate_type, &outputs, &ongoing_codegen.crate_name.as_str()); + let mut compressed = ongoing_codegen.metadata_version.clone(); + let metadata = if crate_type == CrateType::CrateTypeDylib { + DeflateEncoder::new(&mut compressed, Compression::fast()) + .write_all(&ongoing_codegen.metadata.raw_data) + .unwrap(); + &compressed + } else { + &ongoing_codegen.metadata.raw_data + }; + let mut builder = Builder::new(File::create(&output_name).unwrap()); + let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); + builder.append(&header, Cursor::new(metadata)).unwrap(); + } + + sess.abort_if_errors(); + if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) + && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) + { + sess.fatal("Executables are not supported by the metadata-only backend."); + } + Ok(()) + } +} diff --git a/src/librustc_codegen_utils/lib.rs b/src/librustc_codegen_utils/lib.rs new file mode 100644 index 00000000000..0c18571f4ff --- /dev/null +++ b/src/librustc_codegen_utils/lib.rs @@ -0,0 +1,63 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! # Note +//! +//! This API is completely unstable and subject to change. + +#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", + html_favicon_url = "https://doc.rust-lang.org/favicon.ico", + html_root_url = "https://doc.rust-lang.org/nightly/")] + +#![feature(box_patterns)] +#![feature(box_syntax)] +#![feature(custom_attribute)] +#![allow(unused_attributes)] +#![feature(quote)] +#![feature(rustc_diagnostic_macros)] + +extern crate ar; +extern crate flate2; +#[macro_use] +extern crate log; + +#[macro_use] +extern crate rustc; +extern crate rustc_target; +extern crate rustc_mir; +extern crate rustc_incremental; +extern crate syntax; +extern crate syntax_pos; +#[macro_use] extern crate rustc_data_structures; + +pub extern crate rustc as __rustc; + +use rustc::ty::TyCtxt; + +pub mod link; +pub mod codegen_backend; +pub mod symbol_names; +pub mod symbol_names_test; + +/// check for the #[rustc_error] annotation, which forces an +/// error in codegen. This is used to write compile-fail tests +/// that actually test that compilation succeeds without +/// reporting an error. +pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { + if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { + let main_def_id = tcx.hir.local_def_id(id); + + if tcx.has_attr(main_def_id, "rustc_error") { + tcx.sess.span_fatal(span, "compilation successful"); + } + } +} + +__build_diagnostic_array! { librustc_codegen_utils, DIAGNOSTICS } diff --git a/src/librustc_codegen_utils/link.rs b/src/librustc_codegen_utils/link.rs new file mode 100644 index 00000000000..aabe931d79c --- /dev/null +++ b/src/librustc_codegen_utils/link.rs @@ -0,0 +1,191 @@ +// 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use rustc::session::config::{self, OutputFilenames, Input, OutputType}; +use rustc::session::Session; +use rustc::middle::cstore::{self, LinkMeta}; +use rustc::hir::svh::Svh; +use std::path::{Path, PathBuf}; +use syntax::{ast, attr}; +use syntax_pos::Span; + +pub fn out_filename(sess: &Session, + crate_type: config::CrateType, + outputs: &OutputFilenames, + crate_name: &str) + -> PathBuf { + let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); + let out_filename = outputs.outputs.get(&OutputType::Exe) + .and_then(|s| s.to_owned()) + .or_else(|| outputs.single_output_file.clone()) + .unwrap_or(default_filename); + + check_file_is_writeable(&out_filename, sess); + + out_filename +} + +// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers +// check this already -- however, the Linux linker will happily overwrite a +// read-only file. We should be consistent. +pub fn check_file_is_writeable(file: &Path, sess: &Session) { + if !is_writeable(file) { + sess.fatal(&format!("output file {} is not writeable -- check its \ + permissions", file.display())); + } +} + +fn is_writeable(p: &Path) -> bool { + match p.metadata() { + Err(..) => true, + Ok(m) => !m.permissions().readonly() + } +} + +pub fn build_link_meta(crate_hash: Svh) -> LinkMeta { + let r = LinkMeta { + crate_hash, + }; + info!("{:?}", r); + return r; +} + +pub fn find_crate_name(sess: Option<&Session>, + attrs: &[ast::Attribute], + input: &Input) -> String { + let validate = |s: String, span: Option| { + cstore::validate_crate_name(sess, &s, span); + s + }; + + // Look in attributes 100% of the time to make sure the attribute is marked + // as used. After doing this, however, we still prioritize a crate name from + // the command line over one found in the #[crate_name] attribute. If we + // find both we ensure that they're the same later on as well. + let attr_crate_name = attr::find_by_name(attrs, "crate_name") + .and_then(|at| at.value_str().map(|s| (at, s))); + + if let Some(sess) = sess { + if let Some(ref s) = sess.opts.crate_name { + if let Some((attr, name)) = attr_crate_name { + if name != &**s { + let msg = format!("--crate-name and #[crate_name] are \ + required to match, but `{}` != `{}`", + s, name); + sess.span_err(attr.span, &msg); + } + } + return validate(s.clone(), None); + } + } + + if let Some((attr, s)) = attr_crate_name { + return validate(s.to_string(), Some(attr.span)); + } + if let Input::File(ref path) = *input { + if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { + if s.starts_with("-") { + let msg = format!("crate names cannot start with a `-`, but \ + `{}` has a leading hyphen", s); + if let Some(sess) = sess { + sess.err(&msg); + } + } else { + return validate(s.replace("-", "_"), None); + } + } + } + + "rust_out".to_string() +} + +pub fn filename_for_input(sess: &Session, + crate_type: config::CrateType, + crate_name: &str, + outputs: &OutputFilenames) -> PathBuf { + let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); + + match crate_type { + config::CrateTypeRlib => { + outputs.out_directory.join(&format!("lib{}.rlib", libname)) + } + config::CrateTypeCdylib | + config::CrateTypeProcMacro | + config::CrateTypeDylib => { + let (prefix, suffix) = (&sess.target.target.options.dll_prefix, + &sess.target.target.options.dll_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeStaticlib => { + let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix, + &sess.target.target.options.staticlib_suffix); + outputs.out_directory.join(&format!("{}{}{}", prefix, libname, + suffix)) + } + config::CrateTypeExecutable => { + let suffix = &sess.target.target.options.exe_suffix; + let out_filename = outputs.path(OutputType::Exe); + if suffix.is_empty() { + out_filename.to_path_buf() + } else { + out_filename.with_extension(&suffix[1..]) + } + } + } +} + +/// Returns default crate type for target +/// +/// Default crate type is used when crate type isn't provided neither +/// through cmd line arguments nor through crate attributes +/// +/// It is CrateTypeExecutable for all platforms but iOS as there is no +/// way to run iOS binaries anyway without jailbreaking and +/// interaction with Rust code through static library is the only +/// option for now +pub fn default_output_for_target(sess: &Session) -> config::CrateType { + if !sess.target.target.options.executables { + config::CrateTypeStaticlib + } else { + config::CrateTypeExecutable + } +} + +/// Checks if target supports crate_type as output +pub fn invalid_output_for_target(sess: &Session, + crate_type: config::CrateType) -> bool { + match crate_type { + config::CrateTypeCdylib | + config::CrateTypeDylib | + config::CrateTypeProcMacro => { + if !sess.target.target.options.dynamic_linking { + return true + } + if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { + return true + } + } + _ => {} + } + if sess.target.target.options.only_cdylib { + match crate_type { + config::CrateTypeProcMacro | config::CrateTypeDylib => return true, + _ => {} + } + } + if !sess.target.target.options.executables { + if crate_type == config::CrateTypeExecutable { + return true + } + } + + false +} diff --git a/src/librustc_codegen_utils/symbol_names.rs b/src/librustc_codegen_utils/symbol_names.rs new file mode 100644 index 00000000000..1a62f39ae3d --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names.rs @@ -0,0 +1,435 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The Rust Linkage Model and Symbol Names +//! ======================================= +//! +//! The semantic model of Rust linkage is, broadly, that "there's no global +//! namespace" between crates. Our aim is to preserve the illusion of this +//! model despite the fact that it's not *quite* possible to implement on +//! modern linkers. We initially didn't use system linkers at all, but have +//! been convinced of their utility. +//! +//! There are a few issues to handle: +//! +//! - Linkers operate on a flat namespace, so we have to flatten names. +//! We do this using the C++ namespace-mangling technique. Foo::bar +//! symbols and such. +//! +//! - Symbols for distinct items with the same *name* need to get different +//! linkage-names. Examples of this are monomorphizations of functions or +//! items within anonymous scopes that end up having the same path. +//! +//! - Symbols in different crates but with same names "within" the crate need +//! to get different linkage-names. +//! +//! - Symbol names should be deterministic: Two consecutive runs of the +//! compiler over the same code base should produce the same symbol names for +//! the same items. +//! +//! - Symbol names should not depend on any global properties of the code base, +//! so that small modifications to the code base do not result in all symbols +//! changing. In previous versions of the compiler, symbol names incorporated +//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be +//! infeasible when used in conjunction with incremental compilation because +//! small code changes would invalidate all symbols generated previously. +//! +//! - Even symbols from different versions of the same crate should be able to +//! live next to each other without conflict. +//! +//! In order to fulfill the above requirements the following scheme is used by +//! the compiler: +//! +//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit +//! hash value into every exported symbol name. Anything that makes a difference +//! to the symbol being named, but does not show up in the regular path needs to +//! be fed into this hash: +//! +//! - Different monomorphizations of the same item have the same path but differ +//! in their concrete type parameters, so these parameters are part of the +//! data being digested for the symbol hash. +//! +//! - Rust allows items to be defined in anonymous scopes, such as in +//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have +//! the path `foo::bar`, since the anonymous scopes do not contribute to the +//! path of an item. The compiler already handles this case via so-called +//! disambiguating `DefPaths` which use indices to distinguish items with the +//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` +//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation +//! information into the symbol name too, these indices are fed into the +//! symbol hash, so that the above two symbols would end up with different +//! hash values. +//! +//! The two measures described above suffice to avoid intra-crate conflicts. In +//! order to also avoid inter-crate conflicts two more measures are taken: +//! +//! - The name of the crate containing the symbol is prepended to the symbol +//! name, i.e. symbols are "crate qualified". For example, a function `foo` in +//! module `bar` in crate `baz` would get a symbol name like +//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids +//! simple conflicts between functions from different crates. +//! +//! - In order to be able to also use symbols from two versions of the same +//! crate (which naturally also have the same name), a stronger measure is +//! required: The compiler accepts an arbitrary "disambiguator" value via the +//! `-C metadata` commandline argument. This disambiguator is then fed into +//! the symbol hash of every exported item. Consequently, the symbols in two +//! identical crates but with different disambiguators are not in conflict +//! with each other. This facility is mainly intended to be used by build +//! tools like Cargo. +//! +//! A note on symbol name stability +//! ------------------------------- +//! Previous versions of the compiler resorted to feeding NodeIds into the +//! symbol hash in order to disambiguate between items with the same path. The +//! current version of the name generation algorithm takes great care not to do +//! that, since NodeIds are notoriously unstable: A small change to the +//! code base will offset all NodeIds after the change and thus, much as using +//! the SVH in the hash, invalidate an unbounded number of symbol names. This +//! makes re-using previously compiled code for incremental compilation +//! virtually impossible. Thus, symbol hash generation exclusively relies on +//! DefPaths which are much more robust in the face of changes to the code base. + +use rustc::middle::weak_lang_items; +use rustc_mir::monomorphize::Instance; +use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; +use rustc::hir::def_id::{DefId, LOCAL_CRATE}; +use rustc::hir::map as hir_map; +use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; +use rustc::ty::fold::TypeVisitor; +use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; +use rustc::ty::maps::Providers; +use rustc::ty::subst::Substs; +use rustc::hir::map::definitions::DefPathData; +use rustc::util::common::record_time; + +use syntax::attr; +use syntax_pos::symbol::Symbol; + +use std::fmt::Write; + +pub fn provide(providers: &mut Providers) { + *providers = Providers { + def_symbol_name, + symbol_name, + + ..*providers + }; +} + +fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + + // the DefId of the item this name is for + def_id: DefId, + + // instance this name will be for + instance: Instance<'tcx>, + + // type of the item, without any generic + // parameters substituted; this is + // included in the hash as a kind of + // safeguard. + item_type: Ty<'tcx>, + + // values for generic type parameters, + // if any. + substs: &'tcx Substs<'tcx>) + -> u64 { + debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); + + let mut hasher = ty::util::TypeIdHasher::::new(tcx); + + record_time(&tcx.sess.perf_stats.symbol_hash_time, || { + // the main symbol name is not necessarily unique; hash in the + // compiler's internal def-path, guaranteeing each symbol has a + // truly unique path + hasher.hash(tcx.def_path_hash(def_id)); + + // Include the main item-type. Note that, in this case, the + // assertions about `needs_subst` may not hold, but this item-type + // ought to be the same for every reference anyway. + assert!(!item_type.has_erasable_regions()); + hasher.visit_ty(item_type); + + // If this is a function, we hash the signature as well. + // This is not *strictly* needed, but it may help in some + // situations, see the `run-make/a-b-a-linker-guard` test. + if let ty::TyFnDef(..) = item_type.sty { + item_type.fn_sig(tcx).visit_with(&mut hasher); + } + + // also include any type parameters (for generic items) + assert!(!substs.has_erasable_regions()); + assert!(!substs.needs_subst()); + substs.visit_with(&mut hasher); + + let is_generic = substs.types().next().is_some(); + let avoid_cross_crate_conflicts = + // If this is an instance of a generic function, we also hash in + // the ID of the instantiating crate. This avoids symbol conflicts + // in case the same instances is emitted in two crates of the same + // project. + is_generic || + + // If we're dealing with an instance of a function that's inlined from + // another crate but we're marking it as globally shared to our + // compliation (aka we're not making an internal copy in each of our + // codegen units) then this symbol may become an exported (but hidden + // visibility) symbol. This means that multiple crates may do the same + // and we want to be sure to avoid any symbol conflicts here. + match MonoItem::Fn(instance).instantiation_mode(tcx) { + InstantiationMode::GloballyShared { may_conflict: true } => true, + _ => false, + }; + + if avoid_cross_crate_conflicts { + let instantiating_crate = if is_generic { + if !def_id.is_local() && tcx.share_generics() { + // If we are re-using a monomorphization from another crate, + // we have to compute the symbol hash accordingly. + let upstream_monomorphizations = + tcx.upstream_monomorphizations_for(def_id); + + upstream_monomorphizations.and_then(|monos| monos.get(&substs) + .cloned()) + .unwrap_or(LOCAL_CRATE) + } else { + LOCAL_CRATE + } + } else { + LOCAL_CRATE + }; + + hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); + hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); + } + }); + + // 64 bits should be enough to avoid collisions. + hasher.finish() +} + +fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) + -> ty::SymbolName +{ + let mut buffer = SymbolPathBuffer::new(); + item_path::with_forced_absolute_paths(|| { + tcx.push_item_path(&mut buffer, def_id); + }); + buffer.into_interned() +} + +fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> ty::SymbolName +{ + ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } +} + +fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) + -> String +{ + let def_id = instance.def_id(); + let substs = instance.substs; + + debug!("symbol_name(def_id={:?}, substs={:?})", + def_id, substs); + + let node_id = tcx.hir.as_local_node_id(def_id); + + if let Some(id) = node_id { + if *tcx.sess.plugin_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_plugin_registrar_symbol(disambiguator); + } + if *tcx.sess.derive_registrar_fn.get() == Some(id) { + let disambiguator = tcx.sess.local_crate_disambiguator(); + return tcx.sess.generate_derive_registrar_symbol(disambiguator); + } + } + + // FIXME(eddyb) Precompute a custom symbol name based on attributes. + let attrs = tcx.get_attrs(def_id); + let is_foreign = if let Some(id) = node_id { + match tcx.hir.get(id) { + hir_map::NodeForeignItem(_) => true, + _ => false + } + } else { + tcx.is_foreign_item(def_id) + }; + + if let Some(name) = weak_lang_items::link_name(&attrs) { + return name.to_string(); + } + + if is_foreign { + if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { + return name.to_string(); + } + // Don't mangle foreign items. + return tcx.item_name(def_id).to_string(); + } + + if let Some(name) = tcx.codegen_fn_attrs(def_id).export_name { + // Use provided name + return name.to_string(); + } + + if attr::contains_name(&attrs, "no_mangle") { + // Don't mangle + return tcx.item_name(def_id).to_string(); + } + + // We want to compute the "type" of this item. Unfortunately, some + // kinds of items (e.g., closures) don't have an entry in the + // item-type array. So walk back up the find the closest parent + // that DOES have an entry. + let mut ty_def_id = def_id; + let instance_ty; + loop { + let key = tcx.def_key(ty_def_id); + match key.disambiguated_data.data { + DefPathData::TypeNs(_) | + DefPathData::ValueNs(_) => { + instance_ty = tcx.type_of(ty_def_id); + break; + } + _ => { + // if we're making a symbol for something, there ought + // to be a value or type-def or something in there + // *somewhere* + ty_def_id.index = key.parent.unwrap_or_else(|| { + bug!("finding type for {:?}, encountered def-id {:?} with no \ + parent", def_id, ty_def_id); + }); + } + } + } + + // Erase regions because they may not be deterministic when hashed + // and should not matter anyhow. + let instance_ty = tcx.erase_regions(&instance_ty); + + let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); + + SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) +} + +// Follow C++ namespace-mangling style, see +// http://en.wikipedia.org/wiki/Name_mangling for more info. +// +// It turns out that on macOS you can actually have arbitrary symbols in +// function names (at least when given to LLVM), but this is not possible +// when using unix's linker. Perhaps one day when we just use a linker from LLVM +// we won't need to do this name mangling. The problem with name mangling is +// that it seriously limits the available characters. For example we can't +// have things like &T in symbol names when one would theoretically +// want them for things like impls of traits on that type. +// +// To be able to work on all platforms and get *some* reasonable output, we +// use C++ name-mangling. +struct SymbolPathBuffer { + result: String, + temp_buf: String +} + +impl SymbolPathBuffer { + fn new() -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested + result + } + + fn from_interned(symbol: ty::SymbolName) -> Self { + let mut result = SymbolPathBuffer { + result: String::with_capacity(64), + temp_buf: String::with_capacity(16) + }; + result.result.push_str(&symbol.name.as_str()); + result + } + + fn into_interned(self) -> ty::SymbolName { + ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } + } + + fn finish(mut self, hash: u64) -> String { + // E = end name-sequence + let _ = write!(self.result, "17h{:016x}E", hash); + self.result + } +} + +impl ItemPathBuffer for SymbolPathBuffer { + fn root_mode(&self) -> &RootMode { + const ABSOLUTE: &'static RootMode = &RootMode::Absolute; + ABSOLUTE + } + + fn push(&mut self, text: &str) { + self.temp_buf.clear(); + let need_underscore = sanitize(&mut self.temp_buf, text); + let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); + if need_underscore { + self.result.push('_'); + } + self.result.push_str(&self.temp_buf); + } +} + +// Name sanitation. LLVM will happily accept identifiers with weird names, but +// gas doesn't! +// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ +// +// returns true if an underscore must be added at the start +pub fn sanitize(result: &mut String, s: &str) -> bool { + for c in s.chars() { + match c { + // Escape these with $ sequences + '@' => result.push_str("$SP$"), + '*' => result.push_str("$BP$"), + '&' => result.push_str("$RF$"), + '<' => result.push_str("$LT$"), + '>' => result.push_str("$GT$"), + '(' => result.push_str("$LP$"), + ')' => result.push_str("$RP$"), + ',' => result.push_str("$C$"), + + // '.' doesn't occur in types and functions, so reuse it + // for ':' and '-' + '-' | ':' => result.push('.'), + + // These are legal symbols + 'a' ... 'z' + | 'A' ... 'Z' + | '0' ... '9' + | '_' | '.' | '$' => result.push(c), + + _ => { + result.push('$'); + for c in c.escape_unicode().skip(1) { + match c { + '{' => {}, + '}' => result.push('$'), + c => result.push(c), + } + } + } + } + } + + // Underscore-qualify anything that didn't start as an ident. + !result.is_empty() && + result.as_bytes()[0] != '_' as u8 && + ! (result.as_bytes()[0] as char).is_xid_start() +} diff --git a/src/librustc_codegen_utils/symbol_names_test.rs b/src/librustc_codegen_utils/symbol_names_test.rs new file mode 100644 index 00000000000..47bbd67fb5c --- /dev/null +++ b/src/librustc_codegen_utils/symbol_names_test.rs @@ -0,0 +1,79 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Walks the crate looking for items/impl-items/trait-items that have +//! either a `rustc_symbol_name` or `rustc_item_path` attribute and +//! generates an error giving, respectively, the symbol name or +//! item-path. This is used for unit testing the code that generates +//! paths etc in all kinds of annoying scenarios. + +use rustc::hir; +use rustc::ty::TyCtxt; +use syntax::ast; + +use rustc_mir::monomorphize::Instance; + +const SYMBOL_NAME: &'static str = "rustc_symbol_name"; +const ITEM_PATH: &'static str = "rustc_item_path"; + +pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { + // if the `rustc_attrs` feature is not enabled, then the + // attributes we are interested in cannot be present anyway, so + // skip the walk. + if !tcx.features().rustc_attrs { + return; + } + + tcx.dep_graph.with_ignore(|| { + let mut visitor = SymbolNamesTest { tcx: tcx }; + tcx.hir.krate().visit_all_item_likes(&mut visitor); + }) +} + +struct SymbolNamesTest<'a, 'tcx:'a> { + tcx: TyCtxt<'a, 'tcx, 'tcx>, +} + +impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { + fn process_attrs(&mut self, + node_id: ast::NodeId) { + let tcx = self.tcx; + let def_id = tcx.hir.local_def_id(node_id); + for attr in tcx.get_attrs(def_id).iter() { + if attr.check_name(SYMBOL_NAME) { + // for now, can only use on monomorphic names + let instance = Instance::mono(tcx, def_id); + let name = self.tcx.symbol_name(instance); + tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); + } else if attr.check_name(ITEM_PATH) { + let path = tcx.item_path_str(def_id); + tcx.sess.span_err(attr.span, &format!("item-path({})", path)); + } + + // (*) The formatting of `tag({})` is chosen so that tests can elect + // to test the entirety of the string, if they choose, or else just + // some subset. + } + } +} + +impl<'a, 'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'a, 'tcx> { + fn visit_item(&mut self, item: &'tcx hir::Item) { + self.process_attrs(item.id); + } + + fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { + self.process_attrs(trait_item.id); + } + + fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { + self.process_attrs(impl_item.id); + } +} diff --git a/src/librustc_driver/Cargo.toml b/src/librustc_driver/Cargo.toml index 1827533f0ac..24bf07d793f 100644 --- a/src/librustc_driver/Cargo.toml +++ b/src/librustc_driver/Cargo.toml @@ -31,7 +31,7 @@ rustc_privacy = { path = "../librustc_privacy" } rustc_resolve = { path = "../librustc_resolve" } rustc_save_analysis = { path = "../librustc_save_analysis" } rustc_traits = { path = "../librustc_traits" } -rustc_trans_utils = { path = "../librustc_trans_utils" } +rustc_codegen_utils = { path = "../librustc_codegen_utils" } rustc_typeck = { path = "../librustc_typeck" } serialize = { path = "../libserialize" } syntax = { path = "../libsyntax" } diff --git a/src/librustc_driver/driver.rs b/src/librustc_driver/driver.rs index 62b3accc46f..ed28b05c125 100644 --- a/src/librustc_driver/driver.rs +++ b/src/librustc_driver/driver.rs @@ -32,7 +32,7 @@ use rustc_resolve::{MakeGlobMap, Resolver, ResolverArenas}; use rustc_metadata::creader::CrateLoader; use rustc_metadata::cstore::{self, CStore}; use rustc_traits; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use rustc_typeck as typeck; use rustc_privacy; use rustc_plugin::registry::Registry; @@ -110,7 +110,7 @@ pub fn spawn_thread_pool R + sync::Send, R: sync:: } pub fn compile_input( - trans: Box, + codegen_backend: Box, sess: &Session, cstore: &CStore, input_path: &Option, @@ -143,7 +143,7 @@ pub fn compile_input( // We need nested scopes here, because the intermediate results can keep // large chunks of memory alive and we want to free them as soon as // possible to keep the peak memory usage low - let (outputs, ongoing_trans, dep_graph) = { + let (outputs, ongoing_codegen, dep_graph) = { let krate = match phase_1_parse_input(control, sess, input) { Ok(krate) => krate, Err(mut parse_error) => { @@ -162,7 +162,7 @@ pub fn compile_input( let outputs = build_output_filenames(input, outdir, output, &krate.attrs, sess); let crate_name = - ::rustc_trans_utils::link::find_crate_name(Some(sess), &krate.attrs, input); + ::rustc_codegen_utils::link::find_crate_name(Some(sess), &krate.attrs, input); install_panic_hook(); let ExpansionResult { @@ -274,7 +274,7 @@ pub fn compile_input( }; phase_3_run_analysis_passes( - &*trans, + &*codegen_backend, control, sess, cstore, @@ -310,14 +310,14 @@ pub fn compile_input( result?; if log_enabled!(::log::Level::Info) { - println!("Pre-trans"); + println!("Pre-codegen"); tcx.print_debug_stats(); } - let ongoing_trans = phase_4_translate_to_llvm(&*trans, tcx, rx); + let ongoing_codegen = phase_4_codegen(&*codegen_backend, tcx, rx); if log_enabled!(::log::Level::Info) { - println!("Post-trans"); + println!("Post-codegen"); tcx.print_debug_stats(); } @@ -328,7 +328,7 @@ pub fn compile_input( } } - Ok((outputs.clone(), ongoing_trans, tcx.dep_graph.clone())) + Ok((outputs.clone(), ongoing_codegen, tcx.dep_graph.clone())) }, )?? }; @@ -337,7 +337,7 @@ pub fn compile_input( sess.code_stats.borrow().print_type_sizes(); } - trans.join_trans_and_link(ongoing_trans, sess, &dep_graph, &outputs)?; + codegen_backend.join_codegen_and_link(ongoing_codegen, sess, &dep_graph, &outputs)?; if sess.opts.debugging_opts.perf_stats { sess.print_perf_stats(); @@ -1089,7 +1089,7 @@ pub fn default_provide_extern(providers: &mut ty::maps::Providers) { /// miscellaneous analysis passes on the crate. Return various /// structures carrying the results of the analysis. pub fn phase_3_run_analysis_passes<'tcx, F, R>( - trans: &TransCrate, + codegen_backend: &CodegenBackend, control: &CompileController, sess: &'tcx Session, cstore: &'tcx CrateStoreDyn, @@ -1128,12 +1128,12 @@ where let mut local_providers = ty::maps::Providers::default(); default_provide(&mut local_providers); - trans.provide(&mut local_providers); + codegen_backend.provide(&mut local_providers); (control.provide)(&mut local_providers); let mut extern_providers = local_providers; default_provide_extern(&mut extern_providers); - trans.provide_extern(&mut extern_providers); + codegen_backend.provide_extern(&mut extern_providers); (control.provide_extern)(&mut extern_providers); let (tx, rx) = mpsc::channel(); @@ -1233,10 +1233,10 @@ where ) } -/// Run the translation phase to LLVM, after which the AST and analysis can +/// Run the codegen backend, after which the AST and analysis can /// be discarded. -pub fn phase_4_translate_to_llvm<'a, 'tcx>( - trans: &TransCrate, +pub fn phase_4_codegen<'a, 'tcx>( + codegen_backend: &CodegenBackend, tcx: TyCtxt<'a, 'tcx, 'tcx>, rx: mpsc::Receiver>, ) -> Box { @@ -1244,12 +1244,12 @@ pub fn phase_4_translate_to_llvm<'a, 'tcx>( ::rustc::middle::dependency_format::calculate(tcx) }); - let translation = time(tcx.sess, "translation", move || trans.trans_crate(tcx, rx)); + let codegen = time(tcx.sess, "codegen", move || codegen_backend.codegen_crate(tcx, rx)); if tcx.sess.profile_queries() { profile::dump(&tcx.sess, "profile_queries".to_string()) } - translation + codegen } fn escape_dep_filename(filename: &FileName) -> String { @@ -1272,7 +1272,7 @@ fn generated_output_paths( // If the filename has been overridden using `-o`, it will not be modified // by appending `.rlib`, `.exe`, etc., so we can skip this transformation. OutputType::Exe if !exact_name => for crate_type in sess.crate_types.borrow().iter() { - let p = ::rustc_trans_utils::link::filename_for_input( + let p = ::rustc_codegen_utils::link::filename_for_input( sess, *crate_type, crate_name, @@ -1424,7 +1424,7 @@ pub fn collect_crate_types(session: &Session, attrs: &[ast::Attribute]) -> Vec Vec(run_compiler: F) -> isize 0 } -fn load_backend_from_dylib(path: &Path) -> fn() -> Box { +fn load_backend_from_dylib(path: &Path) -> fn() -> Box { // Note that we're specifically using `open_global_now` here rather than // `open`, namely we want the behavior on Unix of RTLD_GLOBAL and RTLD_NOW, // where NOW means "bind everything right now" because we don't want @@ -233,24 +235,24 @@ fn load_backend_from_dylib(path: &Path) -> fn() -> Box { } } -pub fn get_trans(sess: &Session) -> Box { +pub fn get_codegen_backend(sess: &Session) -> Box { static INIT: Once = ONCE_INIT; #[allow(deprecated)] #[no_debug] - static mut LOAD: fn() -> Box = || unreachable!(); + static mut LOAD: fn() -> Box = || unreachable!(); INIT.call_once(|| { - let trans_name = sess.opts.debugging_opts.codegen_backend.as_ref() + let codegen_name = sess.opts.debugging_opts.codegen_backend.as_ref() .unwrap_or(&sess.target.target.options.codegen_backend); - let backend = match &trans_name[..] { + let backend = match &codegen_name[..] { "metadata_only" => { - rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new + rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::new } filename if filename.contains(".") => { load_backend_from_dylib(filename.as_ref()) } - trans_name => get_trans_sysroot(trans_name), + codegen_name => get_codegen_sysroot(codegen_name), }; unsafe { @@ -262,15 +264,15 @@ pub fn get_trans(sess: &Session) -> Box { backend } -fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { +fn get_codegen_sysroot(backend_name: &str) -> fn() -> Box { // For now we only allow this function to be called once as it'll dlopen a // few things, which seems to work best if we only do that once. In - // general this assertion never trips due to the once guard in `get_trans`, + // general this assertion never trips due to the once guard in `get_codegen_backend`, // but there's a few manual calls to this function in this file we protect // against. static LOADED: AtomicBool = ATOMIC_BOOL_INIT; assert!(!LOADED.fetch_or(true, Ordering::SeqCst), - "cannot load the default trans backend twice"); + "cannot load the default codegen backend twice"); // When we're compiling this library with `--test` it'll run as a binary but // not actually exercise much functionality. As a result most of the logic @@ -278,7 +280,7 @@ fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { // let's just return a dummy creation function which won't be used in // general anyway. if cfg!(test) { - return rustc_trans_utils::trans_crate::MetadataOnlyTransCrate::new + return rustc_codegen_utils::codegen_backend::MetadataOnlyCodegenBackend::new } let target = session::config::host_triple(); @@ -346,7 +348,7 @@ fn get_trans_sysroot(backend_name: &str) -> fn() -> Box { let mut file: Option = None; - let expected_name = format!("rustc_trans-{}", backend_name); + let expected_name = format!("rustc_codegen_llvm-{}", backend_name); for entry in d.filter_map(|e| e.ok()) { let path = entry.path(); let filename = match path.file_name().and_then(|s| s.to_str()) { @@ -520,20 +522,20 @@ fn run_compiler_with_pool<'a>( return (Err(CompileIncomplete::Stopped), Some(sess)); } - let trans = get_trans(&sess); + let codegen_backend = get_codegen_backend(&sess); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let result = { let plugins = sess.opts.debugging_opts.extra_plugins.clone(); - let cstore = CStore::new(trans.metadata_loader()); + let cstore = CStore::new(codegen_backend.metadata_loader()); - do_or_return!(callbacks.late_callback(&*trans, + do_or_return!(callbacks.late_callback(&*codegen_backend, &matches, &sess, &cstore, @@ -545,7 +547,7 @@ fn run_compiler_with_pool<'a>( let control = callbacks.build_controller(&sess, &matches); - driver::compile_input(trans, + driver::compile_input(codegen_backend, &sess, &cstore, &input_file_path, @@ -659,7 +661,7 @@ pub trait CompilerCalls<'a> { // be called just before actual compilation starts (and before build_controller // is called), after all arguments etc. have been completely handled. fn late_callback(&mut self, - _: &TransCrate, + _: &CodegenBackend, _: &getopts::Matches, _: &Session, _: &CrateStore, @@ -841,11 +843,11 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, cfg.clone()); - let trans = get_trans(&sess); - target_features::add_configuration(&mut cfg, &sess, &*trans); + let codegen_backend = get_codegen_backend(&sess); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let should_stop = RustcDefaultCalls::print_crate_info( - &*trans, + &*codegen_backend, &sess, None, odir, @@ -863,7 +865,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { } fn late_callback(&mut self, - trans: &TransCrate, + codegen_backend: &CodegenBackend, matches: &getopts::Matches, sess: &Session, cstore: &CrateStore, @@ -871,7 +873,7 @@ impl<'a> CompilerCalls<'a> for RustcDefaultCalls { odir: &Option, ofile: &Option) -> Compilation { - RustcDefaultCalls::print_crate_info(trans, sess, Some(input), odir, ofile) + RustcDefaultCalls::print_crate_info(codegen_backend, sess, Some(input), odir, ofile) .and_then(|| RustcDefaultCalls::list_metadata(sess, cstore, matches, input)) } @@ -1000,7 +1002,7 @@ impl RustcDefaultCalls { } - fn print_crate_info(trans: &TransCrate, + fn print_crate_info(codegen_backend: &CodegenBackend, sess: &Session, input: Option<&Input>, odir: &Option, @@ -1042,14 +1044,14 @@ impl RustcDefaultCalls { }; let attrs = attrs.as_ref().unwrap(); let t_outputs = driver::build_output_filenames(input, odir, ofile, attrs, sess); - let id = rustc_trans_utils::link::find_crate_name(Some(sess), attrs, input); + let id = rustc_codegen_utils::link::find_crate_name(Some(sess), attrs, input); if *req == PrintRequest::CrateName { println!("{}", id); continue; } let crate_types = driver::collect_crate_types(sess, attrs); for &style in &crate_types { - let fname = rustc_trans_utils::link::filename_for_input( + let fname = rustc_codegen_utils::link::filename_for_input( sess, style, &id, @@ -1102,7 +1104,7 @@ impl RustcDefaultCalls { } } RelocationModels | CodeModels | TlsModels | TargetCPUs | TargetFeatures => { - trans.print(*req, sess); + codegen_backend.print(*req, sess); } // Any output here interferes with Cargo's parsing of other printed output PrintRequest::NativeStaticLibs => {} @@ -1143,7 +1145,7 @@ pub fn version(binary: &str, matches: &getopts::Matches) { println!("commit-date: {}", unw(commit_date_str())); println!("host: {}", config::host_triple()); println!("release: {}", unw(release_str())); - get_trans_sysroot("llvm")().print_version(); + get_codegen_sysroot("llvm")().print_version(); } } @@ -1451,7 +1453,7 @@ pub fn handle_options(args: &[String]) -> Option { } if cg_flags.contains(&"passes=list".to_string()) { - get_trans_sysroot("llvm")().print_passes(); + get_codegen_sysroot("llvm")().print_passes(); return None; } @@ -1663,8 +1665,8 @@ pub fn diagnostics_registry() -> errors::registry::Registry { all_errors.extend_from_slice(&rustc_resolve::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_privacy::DIAGNOSTICS); // FIXME: need to figure out a way to get these back in here - // all_errors.extend_from_slice(get_trans(sess).diagnostics()); - all_errors.extend_from_slice(&rustc_trans_utils::DIAGNOSTICS); + // all_errors.extend_from_slice(get_codegen_backend(sess).diagnostics()); + all_errors.extend_from_slice(&rustc_codegen_utils::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_metadata::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_passes::DIAGNOSTICS); all_errors.extend_from_slice(&rustc_plugin::DIAGNOSTICS); diff --git a/src/librustc_driver/pretty.rs b/src/librustc_driver/pretty.rs index 108b4762338..f1ad14237ee 100644 --- a/src/librustc_driver/pretty.rs +++ b/src/librustc_driver/pretty.rs @@ -228,8 +228,8 @@ impl PpSourceMode { } PpmTyped => { let control = &driver::CompileController::basic(); - let trans = ::get_trans(sess); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + let codegen_backend = ::get_codegen_backend(sess); + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, sess, cstore, @@ -1089,8 +1089,8 @@ fn print_with_analysis<'tcx, 'a: 'tcx>(sess: &'a Session, let mut out = Vec::new(); let control = &driver::CompileController::basic(); - let trans = ::get_trans(sess); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + let codegen_backend = ::get_codegen_backend(sess); + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, sess, cstore, diff --git a/src/librustc_driver/test.rs b/src/librustc_driver/test.rs index 78c95a5ce05..46f552ed28d 100644 --- a/src/librustc_driver/test.rs +++ b/src/librustc_driver/test.rs @@ -122,7 +122,7 @@ fn test_env_with_pool( None, diagnostic_handler, Lrc::new(CodeMap::new(FilePathMapping::empty()))); - let cstore = CStore::new(::get_trans(&sess).metadata_loader()); + let cstore = CStore::new(::get_codegen_backend(&sess).metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let input = config::Input::Str { name: FileName::Anon, diff --git a/src/librustc_incremental/assert_dep_graph.rs b/src/librustc_incremental/assert_dep_graph.rs index 38e891008f7..c317d31b95a 100644 --- a/src/librustc_incremental/assert_dep_graph.rs +++ b/src/librustc_incremental/assert_dep_graph.rs @@ -13,7 +13,7 @@ //! will dump graphs in graphviz form to disk, and it searches for //! `#[rustc_if_this_changed]` and `#[rustc_then_this_would_need]` //! annotations. These annotations can be used to test whether paths -//! exist in the graph. These checks run after trans, so they view the +//! exist in the graph. These checks run after codegen, so they view the //! the final state of the dependency graph. Note that there are //! similar assertions found in `persist::dirty_clean` which check the //! **initial** state of the dependency graph, just after it has been @@ -36,10 +36,10 @@ //! #[rustc_if_this_changed(Hir)] //! fn foo() { } //! -//! #[rustc_then_this_would_need(trans)] //~ ERROR no path from `foo` +//! #[rustc_then_this_would_need(codegen)] //~ ERROR no path from `foo` //! fn bar() { } //! -//! #[rustc_then_this_would_need(trans)] //~ ERROR OK +//! #[rustc_then_this_would_need(codegen)] //~ ERROR OK //! fn baz() { foo(); } //! ``` diff --git a/src/librustc_incremental/assert_module_sources.rs b/src/librustc_incremental/assert_module_sources.rs index 6906dacfc5e..df8e0f056af 100644 --- a/src/librustc_incremental/assert_module_sources.rs +++ b/src/librustc_incremental/assert_module_sources.rs @@ -16,7 +16,7 @@ //! //! ``` //! #![rustc_partition_reused(module="spike", cfg="rpass2")] -//! #![rustc_partition_translated(module="spike-x", cfg="rpass2")] +//! #![rustc_partition_codegened(module="spike-x", cfg="rpass2")] //! ``` //! //! The first indicates (in the cfg `rpass2`) that `spike.o` will be @@ -32,13 +32,13 @@ use rustc::mir::mono::CodegenUnit; use rustc::ty::TyCtxt; use syntax::ast; use syntax_pos::symbol::Symbol; -use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_TRANSLATED}; +use rustc::ich::{ATTR_PARTITION_REUSED, ATTR_PARTITION_CODEGENED}; const MODULE: &'static str = "module"; const CFG: &'static str = "cfg"; #[derive(Debug, PartialEq, Clone, Copy)] -enum Disposition { Reused, Translated } +enum Disposition { Reused, Codegened } pub fn assert_module_sources<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { tcx.dep_graph.with_ignore(|| { @@ -61,8 +61,8 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { fn check_attr(&self, attr: &ast::Attribute) { let disposition = if attr.check_name(ATTR_PARTITION_REUSED) { Disposition::Reused - } else if attr.check_name(ATTR_PARTITION_TRANSLATED) { - Disposition::Translated + } else if attr.check_name(ATTR_PARTITION_CODEGENED) { + Disposition::Codegened } else { return; }; @@ -84,17 +84,17 @@ impl<'a, 'tcx> AssertModuleSource<'a, 'tcx> { (Disposition::Reused, false) => { self.tcx.sess.span_err( attr.span, - &format!("expected module named `{}` to be Reused but is Translated", + &format!("expected module named `{}` to be Reused but is Codegened", mname)); } - (Disposition::Translated, true) => { + (Disposition::Codegened, true) => { self.tcx.sess.span_err( attr.span, - &format!("expected module named `{}` to be Translated but is Reused", + &format!("expected module named `{}` to be Codegened but is Reused", mname)); } (Disposition::Reused, true) | - (Disposition::Translated, false) => { + (Disposition::Codegened, false) => { // These are what we would expect. } } diff --git a/src/librustc_incremental/persist/dirty_clean.rs b/src/librustc_incremental/persist/dirty_clean.rs index e114606a631..8f406161831 100644 --- a/src/librustc_incremental/persist/dirty_clean.rs +++ b/src/librustc_incremental/persist/dirty_clean.rs @@ -239,8 +239,8 @@ pub fn check_dirty_clean_annotations<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { intravisit::walk_crate(&mut all_attrs, krate); // Note that we cannot use the existing "unused attribute"-infrastructure - // here, since that is running before trans. This is also the reason why - // all trans-specific attributes are `Whitelisted` in syntax::feature_gate. + // here, since that is running before codegen. This is also the reason why + // all codegen-specific attributes are `Whitelisted` in syntax::feature_gate. all_attrs.report_unchecked_attrs(&dirty_clean_visitor.checked_attrs); }) } diff --git a/src/librustc_lint/types.rs b/src/librustc_lint/types.rs index 32963146893..2abe361233f 100644 --- a/src/librustc_lint/types.rs +++ b/src/librustc_lint/types.rs @@ -432,7 +432,7 @@ enum FfiResult<'tcx> { /// "nullable pointer optimization". Currently restricted /// to function pointers and references, but could be /// expanded to cover NonZero raw pointers and newtypes. -/// FIXME: This duplicates code in trans. +/// FIXME: This duplicates code in codegen. fn is_repr_nullable_ptr<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def: &'tcx ty::AdtDef, substs: &Substs<'tcx>) diff --git a/src/librustc_llvm/ffi.rs b/src/librustc_llvm/ffi.rs index c1e32c7c022..0497de940ca 100644 --- a/src/librustc_llvm/ffi.rs +++ b/src/librustc_llvm/ffi.rs @@ -515,7 +515,7 @@ pub enum ModuleBuffer {} // dllimport/dllexport are applied and need to be correct for everything to // link successfully. The #[link] annotation here says "these symbols are // included statically" which means that they're all exported with dllexport -// and from the rustc_llvm dynamic library. Otherwise the rustc_trans dynamic +// and from the rustc_llvm dynamic library. Otherwise the rustc_codegen_llvm dynamic // library would not be able to access these symbols. #[link(name = "rustllvm", kind = "static")] extern "C" { diff --git a/src/librustc_llvm/lib.rs b/src/librustc_llvm/lib.rs index bf8a087ab55..07830d54d0c 100644 --- a/src/librustc_llvm/lib.rs +++ b/src/librustc_llvm/lib.rs @@ -138,7 +138,7 @@ pub fn SetFunctionCallConv(fn_: ValueRef, cc: CallConv) { } } -// Externally visible symbols that might appear in multiple translation units need to appear in +// Externally visible symbols that might appear in multiple codegen units need to appear in // their own comdat section so that the duplicates can be discarded at link time. This can for // example happen for generics when using multiple codegen units. This function simply uses the // value's name as the comdat value to make sure that it is in a 1-to-1 relationship to the diff --git a/src/librustc_metadata/diagnostics.rs b/src/librustc_metadata/diagnostics.rs index 0a1662dd42d..b38c1235573 100644 --- a/src/librustc_metadata/diagnostics.rs +++ b/src/librustc_metadata/diagnostics.rs @@ -14,7 +14,7 @@ register_long_diagnostics! { E0454: r##" A link name was given with an empty name. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(name = "")] extern {} // error: #[link(name = "")] given with empty name ``` @@ -51,7 +51,7 @@ https://doc.rust-lang.org/book/first-edition/conditional-compilation.html E0458: r##" An unknown "kind" was specified for a link attribute. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(kind = "wonderful_unicorn")] extern {} // error: unknown kind: `wonderful_unicorn` ``` @@ -67,7 +67,7 @@ Please specify a valid "kind" value, from one of the following: E0459: r##" A link was used without a name parameter. Erroneous code example: -```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-trans) +```ignore (cannot-test-this-because-rustdoc-stops-compile-fail-before-codegen) #[link(kind = "dylib")] extern {} // error: #[link(...)] specified without `name = "foo"` ``` diff --git a/src/librustc_metadata/encoder.rs b/src/librustc_metadata/encoder.rs index 3de90abd966..bbc4120f060 100644 --- a/src/librustc_metadata/encoder.rs +++ b/src/librustc_metadata/encoder.rs @@ -868,7 +868,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { fn metadata_output_only(&self) -> bool { // MIR optimisation can be skipped when we're just interested in the metadata. - !self.tcx.sess.opts.output_types.should_trans() + !self.tcx.sess.opts.output_types.should_codegen() } fn const_qualif(&self, mir: u8, body_id: hir::BodyId) -> ConstQualif { @@ -930,7 +930,7 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { hir::ImplItemKind::Method(ref sig, _) => { let generics = self.tcx.generics_of(def_id); let needs_inline = (generics.requires_monomorphization(self.tcx) || - tcx.trans_fn_attrs(def_id).requests_inline()) && + tcx.codegen_fn_attrs(def_id).requests_inline()) && !self.metadata_output_only(); let is_const_fn = sig.constness == hir::Constness::Const; let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; @@ -1224,8 +1224,9 @@ impl<'a, 'b: 'a, 'tcx: 'b> IsolatedEncoder<'a, 'b, 'tcx> { hir::ItemConst(..) => self.encode_optimized_mir(def_id), hir::ItemFn(_, _, constness, _, ref generics, _) => { let has_tps = generics.ty_params().next().is_some(); - let needs_inline = (has_tps || tcx.trans_fn_attrs(def_id).requests_inline()) && - !self.metadata_output_only(); + let needs_inline = + (has_tps || tcx.codegen_fn_attrs(def_id).requests_inline()) && + !self.metadata_output_only(); let always_encode_mir = self.tcx.sess.opts.debugging_opts.always_encode_mir; if needs_inline || constness == hir::Constness::Const || always_encode_mir { self.encode_optimized_mir(def_id) diff --git a/src/librustc_mir/build/expr/as_rvalue.rs b/src/librustc_mir/build/expr/as_rvalue.rs index 20e11abca9f..4115dbe6274 100644 --- a/src/librustc_mir/build/expr/as_rvalue.rs +++ b/src/librustc_mir/build/expr/as_rvalue.rs @@ -140,7 +140,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { block.and(Rvalue::Cast(CastKind::Unsize, source, expr.ty)) } ExprKind::Array { fields } => { - // (*) We would (maybe) be closer to trans if we + // (*) We would (maybe) be closer to codegen if we // handled this and other aggregate cases via // `into()`, not `as_rvalue` -- in that case, instead // of generating diff --git a/src/librustc_mir/build/expr/mod.rs b/src/librustc_mir/build/expr/mod.rs index 025e77343e7..0fd4b8e7e23 100644 --- a/src/librustc_mir/build/expr/mod.rs +++ b/src/librustc_mir/build/expr/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! Translates expressions into MIR. As a caller into this module, you +//! Builds MIR from expressions. As a caller into this module, you //! have many options, but the first thing you have to decide is //! whether you are evaluating this expression for its *value*, its //! *location*, or as a *constant*. @@ -41,7 +41,7 @@ //! ### Implementation notes //! //! For any given kind of expression, there is generally one way that -//! can be translated most naturally. This is specified by the +//! can be lowered most naturally. This is specified by the //! `Category::of` function in the `category` module. For example, a //! struct expression (or other expression that creates a new value) //! is typically easiest to write in terms of `as_rvalue` or `into`, diff --git a/src/librustc_mir/build/matches/mod.rs b/src/librustc_mir/build/matches/mod.rs index 6946ac4c7b2..f3953d0877c 100644 --- a/src/librustc_mir/build/matches/mod.rs +++ b/src/librustc_mir/build/matches/mod.rs @@ -896,7 +896,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { .map(|b| GuardFrameLocal::new(b.var_id, b.binding_mode)) .collect(), }; - debug!("Entering guard translation context: {:?}", guard_frame); + debug!("Entering guard building context: {:?}", guard_frame); self.guard_context.push(guard_frame); } else { self.bind_matched_candidate_for_arm_body(block, &candidate.bindings, false); @@ -909,7 +909,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { let cond = unpack!(block = self.as_local_operand(block, guard)); if autoref { let guard_frame = self.guard_context.pop().unwrap(); - debug!("Exiting guard translation context with locals: {:?}", guard_frame); + debug!("Exiting guard building context with locals: {:?}", guard_frame); } let false_edge_block = self.cfg.start_new_block(); diff --git a/src/librustc_mir/build/mod.rs b/src/librustc_mir/build/mod.rs index afbcf100b05..3bbf73ec8b5 100644 --- a/src/librustc_mir/build/mod.rs +++ b/src/librustc_mir/build/mod.rs @@ -775,7 +775,7 @@ impl<'a, 'gcx, 'tcx> Builder<'a, 'gcx, 'tcx> { /////////////////////////////////////////////////////////////////////////// // Builder methods are broken up into modules, depending on what kind -// of thing is being translated. Note that they use the `unpack` macro +// of thing is being lowered. Note that they use the `unpack` macro // above extensively. mod block; diff --git a/src/librustc_mir/build/scope.rs b/src/librustc_mir/build/scope.rs index a631ab27d1c..5dc59ac9f41 100644 --- a/src/librustc_mir/build/scope.rs +++ b/src/librustc_mir/build/scope.rs @@ -10,7 +10,7 @@ /*! Managing the scope stack. The scopes are tied to lexical scopes, so as -we descend the HAIR, we push a scope on the stack, translate ite +we descend the HAIR, we push a scope on the stack, build its contents, and then pop it off. Every scope is named by a `region::Scope`. @@ -30,7 +30,7 @@ them. Eventually, when we shift to non-lexical lifetimes, there should be no need to remember this mapping. There is one additional wrinkle, actually, that I wanted to hide from -you but duty compels me to mention. In the course of translating +you but duty compels me to mention. In the course of building matches, it sometimes happen that certain code (namely guards) gets executed multiple times. This means that the scope lexical scope may in fact correspond to multiple, disjoint SEME regions. So in fact our @@ -38,7 +38,7 @@ mapping is from one scope to a vector of SEME regions. ### Drops -The primary purpose for scopes is to insert drops: while translating +The primary purpose for scopes is to insert drops: while building the contents, we also accumulate places that need to be dropped upon exit from each scope. This is done by calling `schedule_drop`. Once a drop is scheduled, whenever we branch out we will insert drops of all diff --git a/src/librustc_mir/hair/cx/block.rs b/src/librustc_mir/hair/cx/block.rs index 14aa307f0ae..5ef1eef133d 100644 --- a/src/librustc_mir/hair/cx/block.rs +++ b/src/librustc_mir/hair/cx/block.rs @@ -20,7 +20,7 @@ impl<'tcx> Mirror<'tcx> for &'tcx hir::Block { type Output = Block<'tcx>; fn make_mirror<'a, 'gcx>(self, cx: &mut Cx<'a, 'gcx, 'tcx>) -> Block<'tcx> { - // We have to eagerly translate the "spine" of the statements + // We have to eagerly lower the "spine" of the statements // in order to get the lexical scoping correctly. let stmts = mirror_stmts(cx, self.hir_id.local_id, &*self.stmts); let opt_destruction_scope = diff --git a/src/librustc_mir/hair/cx/mod.rs b/src/librustc_mir/hair/cx/mod.rs index 4765a82d85b..b01b3542136 100644 --- a/src/librustc_mir/hair/cx/mod.rs +++ b/src/librustc_mir/hair/cx/mod.rs @@ -77,7 +77,7 @@ impl<'a, 'gcx, 'tcx> Cx<'a, 'gcx, 'tcx> { // Some functions always have overflow checks enabled, // however, they may not get codegen'd, depending on - // the settings for the crate they are translated in. + // the settings for the crate they are codegened in. let mut check_overflow = attr::contains_name(attrs, "rustc_inherit_overflow_checks"); // Respect -C overflow-checks. diff --git a/src/librustc_mir/hair/mod.rs b/src/librustc_mir/hair/mod.rs index c27250267bb..ab9acdc4950 100644 --- a/src/librustc_mir/hair/mod.rs +++ b/src/librustc_mir/hair/mod.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! The MIR is translated from some high-level abstract IR +//! The MIR is built from some high-level abstract IR //! (HAIR). This section defines the HAIR along with a trait for //! accessing it. The intention is to allow MIR construction to be //! unit-tested and separated from the Rust source and compiler data @@ -107,12 +107,12 @@ pub enum StmtKind<'tcx> { }, } -/// The Hair trait implementor translates their expressions (`&'tcx H::Expr`) -/// into instances of this `Expr` enum. This translation can be done +/// The Hair trait implementor lowers their expressions (`&'tcx H::Expr`) +/// into instances of this `Expr` enum. This lowering can be done /// basically as lazily or as eagerly as desired: every recursive /// reference to an expression in this enum is an `ExprRef<'tcx>`, which /// may in turn be another instance of this enum (boxed), or else an -/// untranslated `&'tcx H::Expr`. Note that instances of `Expr` are very +/// unlowered `&'tcx H::Expr`. Note that instances of `Expr` are very /// shortlived. They are created by `Hair::to_expr`, analyzed and /// converted into MIR, and then discarded. /// diff --git a/src/librustc_mir/monomorphize/collector.rs b/src/librustc_mir/monomorphize/collector.rs index dc2009a0260..c62eb1cf185 100644 --- a/src/librustc_mir/monomorphize/collector.rs +++ b/src/librustc_mir/monomorphize/collector.rs @@ -188,7 +188,7 @@ //! this is not implemented however: a mono item will be produced //! regardless of whether it is actually needed or not. -use rustc::hir::{self, TransFnAttrFlags}; +use rustc::hir::{self, CodegenFnAttrFlags}; use rustc::hir::itemlikevisit::ItemLikeVisitor; use rustc::hir::map as hir_map; @@ -343,9 +343,9 @@ fn collect_roots<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, visitor.push_extra_entry_roots(); } - // We can only translate items that are instantiable - items all of + // We can only codegen items that are instantiable - items all of // whose predicates hold. Luckily, items that aren't instantiable - // can't actually be used, so we can just skip translating them. + // can't actually be used, so we can just skip codegenning them. roots.retain(|root| root.is_instantiable(tcx)); roots @@ -717,7 +717,7 @@ fn visit_instance_use<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } } -// Returns true if we should translate an instance in the local crate. +// Returns true if we should codegen an instance in the local crate. // Returns false if we can just link to the upstream crate and therefore don't // need a mono item. fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: &Instance<'tcx>) @@ -734,7 +734,7 @@ fn should_monomorphize_locally<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: return match tcx.hir.get_if_local(def_id) { Some(hir_map::NodeForeignItem(..)) => { - false // foreign items are linked against, not translated. + false // foreign items are linked against, not codegened. } Some(_) => true, None => { @@ -1015,8 +1015,8 @@ impl<'b, 'a, 'v> RootCollector<'b, 'a, 'v> { MonoItemCollectionMode::Lazy => { self.entry_fn == Some(def_id) || self.tcx.is_reachable_non_generic(def_id) || - self.tcx.trans_fn_attrs(def_id).flags.contains( - TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) + self.tcx.codegen_fn_attrs(def_id).flags.contains( + CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL) } } } diff --git a/src/librustc_mir/monomorphize/item.rs b/src/librustc_mir/monomorphize/item.rs index a569ad00d0c..27f8254bf8a 100644 --- a/src/librustc_mir/monomorphize/item.rs +++ b/src/librustc_mir/monomorphize/item.rs @@ -29,7 +29,7 @@ use syntax_pos::symbol::Symbol; use syntax::codemap::Span; pub use rustc::mir::mono::MonoItem; -/// Describes how a translation item will be instantiated in object files. +/// Describes how a monomorphization will be instantiated in object files. #[derive(PartialEq, Eq, Clone, Copy, Debug, Hash)] pub enum InstantiationMode { /// There will be exactly one instance of the given MonoItem. It will have @@ -37,7 +37,7 @@ pub enum InstantiationMode { GloballyShared { /// In some compilation scenarios we may decide to take functions that /// are typically `LocalCopy` and instead move them to `GloballyShared` - /// to avoid translating them a bunch of times. In this situation, + /// to avoid codegenning them a bunch of times. In this situation, /// however, our local copy may conflict with other crates also /// inlining the same function. /// @@ -114,7 +114,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { // creating one copy of this `#[inline]` function which may // conflict with upstream crates as it could be an exported // symbol. - match tcx.trans_fn_attrs(instance.def_id()).inline { + match tcx.codegen_fn_attrs(instance.def_id()).inline { InlineAttr::Always => InstantiationMode::LocalCopy, _ => { InstantiationMode::GloballyShared { may_conflict: true } @@ -137,19 +137,19 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { MonoItem::GlobalAsm(..) => return None, }; - let trans_fn_attrs = tcx.trans_fn_attrs(def_id); - trans_fn_attrs.linkage + let codegen_fn_attrs = tcx.codegen_fn_attrs(def_id); + codegen_fn_attrs.linkage } /// Returns whether this instance is instantiable - whether it has no unsatisfied /// predicates. /// - /// In order to translate an item, all of its predicates must hold, because + /// In order to codegen an item, all of its predicates must hold, because /// otherwise the item does not make sense. Type-checking ensures that /// the predicates of every item that is *used by* a valid item *do* /// hold, so we can rely on that. /// - /// However, we translate collector roots (reachable items) and functions + /// However, we codegen collector roots (reachable items) and functions /// in vtables when they are seen, even if they are not used, and so they /// might not be instantiable. For example, a programmer can define this /// public function: @@ -158,7 +158,7 @@ pub trait MonoItemExt<'a, 'tcx>: fmt::Debug { /// <&mut () as Clone>::clone(&s); /// } /// - /// That function can't be translated, because the method `<&mut () as Clone>::clone` + /// That function can't be codegened, because the method `<&mut () as Clone>::clone` /// does not exist. Luckily for us, that function can't ever be used, /// because that would require for `&'a mut (): Clone` to hold, so we /// can just not emit any code, or even a linker reference for it. @@ -229,7 +229,7 @@ impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> { // MonoItem String Keys //=----------------------------------------------------------------------------- -// The code below allows for producing a unique string key for a trans item. +// The code below allows for producing a unique string key for a mono item. // These keys are used by the handwritten auto-tests, so they need to be // predictable and human-readable. // diff --git a/src/librustc_mir/monomorphize/mod.rs b/src/librustc_mir/monomorphize/mod.rs index 04d4f7a3968..51793305513 100644 --- a/src/librustc_mir/monomorphize/mod.rs +++ b/src/librustc_mir/monomorphize/mod.rs @@ -23,11 +23,11 @@ pub mod item; pub mod partitioning; #[inline(never)] // give this a place in the profiler -pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, trans_items: I) +pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, mono_items: I) where I: Iterator> { - let mut symbols: Vec<_> = trans_items.map(|trans_item| { - (trans_item, trans_item.symbol_name(tcx)) + let mut symbols: Vec<_> = mono_items.map(|mono_item| { + (mono_item, mono_item.symbol_name(tcx)) }).collect(); (&mut symbols[..]).sort_by(|&(_, ref sym1), &(_, ref sym2)|{ @@ -39,11 +39,11 @@ pub fn assert_symbols_are_distinct<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, tra let sym2 = &pair[1].1; if *sym1 == *sym2 { - let trans_item1 = pair[0].0; - let trans_item2 = pair[1].0; + let mono_item1 = pair[0].0; + let mono_item2 = pair[1].0; - let span1 = trans_item1.local_span(tcx); - let span2 = trans_item2.local_span(tcx); + let span1 = mono_item1.local_span(tcx); + let span2 = mono_item2.local_span(tcx); // Deterministically select one of the spans for error reporting let span = match (span1, span2) { @@ -111,7 +111,7 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, } (ty::ClosureKind::Fn, ty::ClosureKind::FnMut) => { // The closure fn `llfn` is a `fn(&self, ...)`. We want a - // `fn(&mut self, ...)`. In fact, at trans time, these are + // `fn(&mut self, ...)`. In fact, at codegen time, these are // basically the same thing, so we can just return llfn. Ok(false) } @@ -124,7 +124,7 @@ fn needs_fn_once_adapter_shim(actual_closure_kind: ty::ClosureKind, // fn call_once(self, ...) { call_mut(&self, ...) } // fn call_once(mut self, ...) { call_mut(&mut self, ...) } // - // These are both the same at trans time. + // These are both the same at codegen time. Ok(true) } _ => Err(()), @@ -167,7 +167,7 @@ pub fn custom_coerce_unsize_info<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, substs: tcx.mk_substs_trait(source_ty, &[target_ty]) }); - match tcx.trans_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) { + match tcx.codegen_fulfill_obligation( (ty::ParamEnv::reveal_all(), trait_ref)) { traits::VtableImpl(traits::VtableImplData { impl_def_id, .. }) => { tcx.coerce_unsized_info(impl_def_id).custom_kind.unwrap() } diff --git a/src/librustc_mir/monomorphize/partitioning.rs b/src/librustc_mir/monomorphize/partitioning.rs index 3a65cd4ea77..9607a84124a 100644 --- a/src/librustc_mir/monomorphize/partitioning.rs +++ b/src/librustc_mir/monomorphize/partitioning.rs @@ -11,16 +11,16 @@ //! Partitioning Codegen Units for Incremental Compilation //! ====================================================== //! -//! The task of this module is to take the complete set of translation items of +//! The task of this module is to take the complete set of monomorphizations of //! a crate and produce a set of codegen units from it, where a codegen unit -//! is a named set of (translation-item, linkage) pairs. That is, this module -//! decides which translation item appears in which codegen units with which +//! is a named set of (mono-item, linkage) pairs. That is, this module +//! decides which monomorphization appears in which codegen units with which //! linkage. The following paragraphs describe some of the background on the //! partitioning scheme. //! //! The most important opportunity for saving on compilation time with -//! incremental compilation is to avoid re-translating and re-optimizing code. -//! Since the unit of translation and optimization for LLVM is "modules" or, how +//! incremental compilation is to avoid re-codegenning and re-optimizing code. +//! Since the unit of codegen and optimization for LLVM is "modules" or, how //! we call them "codegen units", the particulars of how much time can be saved //! by incremental compilation are tightly linked to how the output program is //! partitioned into these codegen units prior to passing it to LLVM -- @@ -214,17 +214,17 @@ fn fallback_cgu_name(tcx: TyCtxt) -> InternedString { pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_items: I, + mono_items: I, strategy: PartitioningStrategy, inlining_map: &InliningMap<'tcx>) -> Vec> where I: Iterator> { - // In the first step, we place all regular translation items into their - // respective 'home' codegen unit. Regular translation items are all + // In the first step, we place all regular monomorphizations into their + // respective 'home' codegen unit. Regular monomorphizations are all // functions and statics defined in the local crate. - let mut initial_partitioning = place_root_translation_items(tcx, - trans_items); + let mut initial_partitioning = place_root_mono_items(tcx, + mono_items); initial_partitioning.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); @@ -239,10 +239,10 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } // In the next step, we use the inlining map to determine which additional - // translation items have to go into each codegen unit. These additional - // translation items can be drop-glue, functions from external crates, and + // monomorphizations have to go into each codegen unit. These additional + // monomorphizations can be drop-glue, functions from external crates, and // local functions the definition of which is marked with #[inline]. - let mut post_inlining = place_inlined_translation_items(initial_partitioning, + let mut post_inlining = place_inlined_mono_items(initial_partitioning, inlining_map); post_inlining.codegen_units.iter_mut().for_each(|cgu| cgu.estimate_size(&tcx)); @@ -258,7 +258,7 @@ pub fn partition<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, // Finally, sort by codegen unit name, so that we get deterministic results let PostInliningPartitioning { codegen_units: mut result, - trans_item_placements: _, + mono_item_placements: _, internalization_candidates: _, } = post_inlining; @@ -275,23 +275,23 @@ struct PreInliningPartitioning<'tcx> { internalization_candidates: FxHashSet>, } -/// For symbol internalization, we need to know whether a symbol/trans-item is +/// For symbol internalization, we need to know whether a symbol/mono-item is /// accessed from outside the codegen unit it is defined in. This type is used /// to keep track of that. #[derive(Clone, PartialEq, Eq, Debug)] -enum TransItemPlacement { +enum MonoItemPlacement { SingleCgu { cgu_name: InternedString }, MultipleCgus, } struct PostInliningPartitioning<'tcx> { codegen_units: Vec>, - trans_item_placements: FxHashMap, TransItemPlacement>, + mono_item_placements: FxHashMap, MonoItemPlacement>, internalization_candidates: FxHashSet>, } -fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_items: I) +fn place_root_mono_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_items: I) -> PreInliningPartitioning<'tcx> where I: Iterator> { @@ -307,15 +307,15 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, let export_generics = tcx.share_generics() && tcx.local_crate_exports_generics(); - for trans_item in trans_items { - match trans_item.instantiation_mode(tcx) { + for mono_item in mono_items { + match mono_item.instantiation_mode(tcx) { InstantiationMode::GloballyShared { .. } => {} InstantiationMode::LocalCopy => continue, } - let characteristic_def_id = characteristic_def_id_of_trans_item(tcx, trans_item); + let characteristic_def_id = characteristic_def_id_of_mono_item(tcx, mono_item); let is_volatile = is_incremental_build && - trans_item.is_generic_fn(); + mono_item.is_generic_fn(); let codegen_unit_name = match characteristic_def_id { Some(def_id) => compute_codegen_unit_name(tcx, def_id, is_volatile), @@ -353,10 +353,10 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, Visibility::Hidden } }; - let (linkage, mut visibility) = match trans_item.explicit_linkage(tcx) { + let (linkage, mut visibility) = match mono_item.explicit_linkage(tcx) { Some(explicit_linkage) => (explicit_linkage, Visibility::Default), None => { - match trans_item { + match mono_item { MonoItem::Fn(ref instance) => { let visibility = match instance.def { InstanceDef::Item(def_id) => { @@ -463,11 +463,11 @@ fn place_root_translation_items<'a, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, } }; if visibility == Visibility::Hidden && can_be_internalized { - internalization_candidates.insert(trans_item); + internalization_candidates.insert(mono_item); } - codegen_unit.items_mut().insert(trans_item, (linkage, visibility)); - roots.insert(trans_item); + codegen_unit.items_mut().insert(mono_item, (linkage, visibility)); + roots.insert(mono_item); } // always ensure we have at least one CGU; otherwise, if we have a @@ -522,11 +522,11 @@ fn merge_codegen_units<'tcx>(initial_partitioning: &mut PreInliningPartitioning< } } -fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, +fn place_inlined_mono_items<'tcx>(initial_partitioning: PreInliningPartitioning<'tcx>, inlining_map: &InliningMap<'tcx>) -> PostInliningPartitioning<'tcx> { let mut new_partitioning = Vec::new(); - let mut trans_item_placements = FxHashMap(); + let mut mono_item_placements = FxHashMap(); let PreInliningPartitioning { codegen_units: initial_cgus, @@ -545,40 +545,40 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit let mut new_codegen_unit = CodegenUnit::new(old_codegen_unit.name().clone()); - // Add all translation items that are not already there - for trans_item in reachable { - if let Some(linkage) = old_codegen_unit.items().get(&trans_item) { + // Add all monomorphizations that are not already there + for mono_item in reachable { + if let Some(linkage) = old_codegen_unit.items().get(&mono_item) { // This is a root, just copy it over - new_codegen_unit.items_mut().insert(trans_item, *linkage); + new_codegen_unit.items_mut().insert(mono_item, *linkage); } else { - if roots.contains(&trans_item) { - bug!("GloballyShared trans-item inlined into other CGU: \ - {:?}", trans_item); + if roots.contains(&mono_item) { + bug!("GloballyShared mono-item inlined into other CGU: \ + {:?}", mono_item); } // This is a cgu-private copy new_codegen_unit.items_mut().insert( - trans_item, + mono_item, (Linkage::Internal, Visibility::Default), ); } if !single_codegen_unit { // If there is more than one codegen unit, we need to keep track - // in which codegen units each translation item is placed: - match trans_item_placements.entry(trans_item) { + // in which codegen units each monomorphization is placed: + match mono_item_placements.entry(mono_item) { Entry::Occupied(e) => { let placement = e.into_mut(); debug_assert!(match *placement { - TransItemPlacement::SingleCgu { ref cgu_name } => { + MonoItemPlacement::SingleCgu { ref cgu_name } => { *cgu_name != *new_codegen_unit.name() } - TransItemPlacement::MultipleCgus => true, + MonoItemPlacement::MultipleCgus => true, }); - *placement = TransItemPlacement::MultipleCgus; + *placement = MonoItemPlacement::MultipleCgus; } Entry::Vacant(e) => { - e.insert(TransItemPlacement::SingleCgu { + e.insert(MonoItemPlacement::SingleCgu { cgu_name: new_codegen_unit.name().clone() }); } @@ -591,18 +591,18 @@ fn place_inlined_translation_items<'tcx>(initial_partitioning: PreInliningPartit return PostInliningPartitioning { codegen_units: new_partitioning, - trans_item_placements, + mono_item_placements, internalization_candidates, }; - fn follow_inlining<'tcx>(trans_item: MonoItem<'tcx>, + fn follow_inlining<'tcx>(mono_item: MonoItem<'tcx>, inlining_map: &InliningMap<'tcx>, visited: &mut FxHashSet>) { - if !visited.insert(trans_item) { + if !visited.insert(mono_item) { return; } - inlining_map.with_inlining_candidates(trans_item, |target| { + inlining_map.with_inlining_candidates(mono_item, |target| { follow_inlining(target, inlining_map, visited); }); } @@ -625,7 +625,7 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, return; } - // Build a map from every translation item to all the translation items that + // Build a map from every monomorphization to all the monomorphizations that // reference it. let mut accessor_map: FxHashMap, Vec>> = FxHashMap(); inlining_map.iter_accesses(|accessor, accessees| { @@ -636,12 +636,12 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, } }); - let trans_item_placements = &partitioning.trans_item_placements; + let mono_item_placements = &partitioning.mono_item_placements; // For each internalization candidates in each codegen unit, check if it is // accessed from outside its defining codegen unit. for cgu in &mut partitioning.codegen_units { - let home_cgu = TransItemPlacement::SingleCgu { + let home_cgu = MonoItemPlacement::SingleCgu { cgu_name: cgu.name().clone() }; @@ -650,14 +650,14 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, // This item is no candidate for internalizing, so skip it. continue } - debug_assert_eq!(trans_item_placements[accessee], home_cgu); + debug_assert_eq!(mono_item_placements[accessee], home_cgu); if let Some(accessors) = accessor_map.get(accessee) { if accessors.iter() .filter_map(|accessor| { // Some accessors might not have been // instantiated. We can safely ignore those. - trans_item_placements.get(accessor) + mono_item_placements.get(accessor) }) .any(|placement| *placement != home_cgu) { // Found an accessor from another CGU, so skip to the next @@ -667,16 +667,16 @@ fn internalize_symbols<'a, 'tcx>(_tcx: TyCtxt<'a, 'tcx, 'tcx>, } // If we got here, we did not find any accesses from other CGUs, - // so it's fine to make this translation item internal. + // so it's fine to make this monomorphization internal. *linkage_and_visibility = (Linkage::Internal, Visibility::Default); } } } -fn characteristic_def_id_of_trans_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - trans_item: MonoItem<'tcx>) +fn characteristic_def_id_of_mono_item<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, + mono_item: MonoItem<'tcx>) -> Option { - match trans_item { + match mono_item { MonoItem::Fn(instance) => { let def_id = match instance.def { ty::InstanceDef::Item(def_id) => def_id, @@ -771,14 +771,14 @@ fn debug_dump<'a, 'b, 'tcx, I>(tcx: TyCtxt<'a, 'tcx, 'tcx>, for cgu in cgus { debug!("CodegenUnit {}:", cgu.name()); - for (trans_item, linkage) in cgu.items() { - let symbol_name = trans_item.symbol_name(tcx).name.as_str(); + for (mono_item, linkage) in cgu.items() { + let symbol_name = mono_item.symbol_name(tcx).name.as_str(); let symbol_hash_start = symbol_name.rfind('h'); let symbol_hash = symbol_hash_start.map(|i| &symbol_name[i ..]) .unwrap_or(""); debug!(" - {} [{:?}] [{}]", - trans_item.to_string(tcx), + mono_item.to_string(tcx), linkage, symbol_hash); } diff --git a/src/librustc_mir/shim.rs b/src/librustc_mir/shim.rs index c79298d8dd2..2d2663bc7c3 100644 --- a/src/librustc_mir/shim.rs +++ b/src/librustc_mir/shim.rs @@ -69,8 +69,8 @@ fn make_shim<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ) } ty::InstanceDef::Virtual(def_id, _) => { - // We are translating a call back to our def-id, which - // trans::mir knows to turn to an actual virtual call. + // We are generating a call back to our def-id, which the + // codegen backend knows to turn to an actual virtual call. build_call_shim( tcx, def_id, diff --git a/src/librustc_mir/transform/add_call_guards.rs b/src/librustc_mir/transform/add_call_guards.rs index 5be369f85bc..26927904440 100644 --- a/src/librustc_mir/transform/add_call_guards.rs +++ b/src/librustc_mir/transform/add_call_guards.rs @@ -30,7 +30,7 @@ pub use self::AddCallGuards::*; * do at the end of the predecessor block, or at the start of the * successor block. Critical edges have to be broken in order to prevent * "edge actions" from affecting other edges. We need this for calls that are - * translated to LLVM invoke instructions, because invoke is a block terminator + * codegened to LLVM invoke instructions, because invoke is a block terminator * in LLVM so we can't insert any code to handle the call's result into the * block that performs the call. * diff --git a/src/librustc_mir/transform/elaborate_drops.rs b/src/librustc_mir/transform/elaborate_drops.rs index 56050318ca7..15b0c4a8bf7 100644 --- a/src/librustc_mir/transform/elaborate_drops.rs +++ b/src/librustc_mir/transform/elaborate_drops.rs @@ -442,7 +442,7 @@ impl<'b, 'tcx> ElaborateDropsCtxt<'b, 'tcx> { } /// Elaborate a MIR `replace` terminator. This instruction - /// is not directly handled by translation, and therefore + /// is not directly handled by codegen, and therefore /// must be desugared. /// /// The desugaring drops the location if needed, and then writes diff --git a/src/librustc_mir/transform/erase_regions.rs b/src/librustc_mir/transform/erase_regions.rs index cd5ebae2d9d..c697391d867 100644 --- a/src/librustc_mir/transform/erase_regions.rs +++ b/src/librustc_mir/transform/erase_regions.rs @@ -9,7 +9,7 @@ // except according to those terms. //! This pass erases all early-bound regions from the types occurring in the MIR. -//! We want to do this once just before trans, so trans does not have to take +//! We want to do this once just before codegen, so codegen does not have to take //! care erasing regions all over the place. //! NOTE: We do NOT erase regions of statements that are relevant for //! "types-as-contracts"-validation, namely, AcquireValid, ReleaseValid, and EndRegion. diff --git a/src/librustc_mir/transform/inline.rs b/src/librustc_mir/transform/inline.rs index ee6d42b1fe5..5fb9148d49a 100644 --- a/src/librustc_mir/transform/inline.rs +++ b/src/librustc_mir/transform/inline.rs @@ -11,7 +11,7 @@ //! Inlining pass for MIR functions use rustc::hir; -use rustc::hir::TransFnAttrFlags; +use rustc::hir::CodegenFnAttrFlags; use rustc::hir::def_id::DefId; use rustc_data_structures::bitvec::BitVector; @@ -211,16 +211,16 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { return false; } - // Do not inline {u,i}128 lang items, trans const eval depends + // Do not inline {u,i}128 lang items, codegen const eval depends // on detecting calls to these lang items and intercepting them if tcx.is_binop_lang_item(callsite.callee).is_some() { debug!(" not inlining 128bit integer lang item"); return false; } - let trans_fn_attrs = tcx.trans_fn_attrs(callsite.callee); + let codegen_fn_attrs = tcx.codegen_fn_attrs(callsite.callee); - let hinted = match trans_fn_attrs.inline { + let hinted = match codegen_fn_attrs.inline { // Just treat inline(always) as a hint for now, // there are cases that prevent inlining that we // need to check for first. @@ -250,7 +250,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { }; // Significantly lower the threshold for inlining cold functions - if trans_fn_attrs.flags.contains(TransFnAttrFlags::COLD) { + if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::COLD) { threshold /= 5; } @@ -355,7 +355,7 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { } } - if let attr::InlineAttr::Always = trans_fn_attrs.inline { + if let attr::InlineAttr::Always = codegen_fn_attrs.inline { debug!("INLINING {:?} because inline(always) [cost={}]", callsite, cost); true } else { @@ -515,8 +515,8 @@ impl<'a, 'tcx> Inliner<'a, 'tcx> { // Fn::call(closure_ref, tuple_tmp) // // meanwhile the closure body expects the arguments (here, `a`, `b`, and `c`) - // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, trans has - // the job of unpacking this tuple. But here, we are trans. =) So we want to create + // as distinct arguments. (This is the "rust-call" ABI hack.) Normally, codegen has + // the job of unpacking this tuple. But here, we are codegen. =) So we want to create // a vector like // // [closure_ref, tuple_tmp.0, tuple_tmp.1, tuple_tmp.2] diff --git a/src/librustc_mir/transform/mod.rs b/src/librustc_mir/transform/mod.rs index 63ca35aa0e7..e2f2312dbd2 100644 --- a/src/librustc_mir/transform/mod.rs +++ b/src/librustc_mir/transform/mod.rs @@ -277,7 +277,7 @@ fn optimized_mir<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) -> &'tcx simplify::SimplifyLocals, add_call_guards::CriticalCallEdges, - dump_mir::Marker("PreTrans"), + dump_mir::Marker("PreCodegen"), ]; tcx.alloc_mir(mir) } diff --git a/src/librustc_mir/transform/simplify.rs b/src/librustc_mir/transform/simplify.rs index 691fdd130e5..7e23a5cb1a8 100644 --- a/src/librustc_mir/transform/simplify.rs +++ b/src/librustc_mir/transform/simplify.rs @@ -15,7 +15,7 @@ //! //! The `SimplifyLocals` pass is kinda expensive and therefore not very suitable to be run often. //! Most of the passes should not care or be impacted in meaningful ways due to extra locals -//! either, so running the pass once, right before translation, should suffice. +//! either, so running the pass once, right before codegen, should suffice. //! //! On the other side of the spectrum, the `SimplifyCfg` pass is considerably cheap to run, thus //! one should run it after every pass which may modify CFG in significant ways. This pass must diff --git a/src/librustc_trans/Cargo.toml b/src/librustc_trans/Cargo.toml deleted file mode 100644 index 64d3a4f4d53..00000000000 --- a/src/librustc_trans/Cargo.toml +++ /dev/null @@ -1,49 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_trans" -version = "0.0.0" - -[lib] -name = "rustc_trans" -path = "lib.rs" -crate-type = ["dylib"] -test = false - -[dependencies] -bitflags = "1.0.1" -cc = "1.0.1" -flate2 = "1.0" -jobserver = "0.1.5" -libc = "0.2" -log = "0.4" -num_cpus = "1.0" -rustc = { path = "../librustc" } -rustc-demangle = "0.1.4" -rustc_allocator = { path = "../librustc_allocator" } -rustc_apfloat = { path = "../librustc_apfloat" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_errors = { path = "../librustc_errors" } -rustc_incremental = { path = "../librustc_incremental" } -rustc_llvm = { path = "../librustc_llvm" } -rustc_platform_intrinsics = { path = "../librustc_platform_intrinsics" } -rustc_trans_utils = { path = "../librustc_trans_utils" } -rustc_mir = { path = "../librustc_mir" } -serialize = { path = "../libserialize" } -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } -tempdir = "0.3" - -# not actually used but needed to make sure we enable the same feature set as -# winapi used in librustc -env_logger = { version = "0.5", default-features = false } - -[features] -# Used to communicate the feature to `rustc_target` in the same manner that the -# `rustc` driver script communicate this. -jemalloc = ["rustc_target/jemalloc"] - -# This is used to convince Cargo to separately cache builds of `rustc_trans` -# when this option is enabled or not. That way we can build two, cache two -# artifacts, and have nice speedy rebuilds. -emscripten = ["rustc_llvm/emscripten"] diff --git a/src/librustc_trans/README.md b/src/librustc_trans/README.md deleted file mode 100644 index d1868ba2abb..00000000000 --- a/src/librustc_trans/README.md +++ /dev/null @@ -1,7 +0,0 @@ -The `trans` crate contains the code to convert from MIR into LLVM IR, -and then from LLVM IR into machine code. In general it contains code -that runs towards the end of the compilation process. - -For more information about how trans works, see the [rustc guide]. - -[rustc guide]: https://rust-lang-nursery.github.io/rustc-guide/trans.html diff --git a/src/librustc_trans/abi.rs b/src/librustc_trans/abi.rs deleted file mode 100644 index 25c598c532c..00000000000 --- a/src/librustc_trans/abi.rs +++ /dev/null @@ -1,696 +0,0 @@ -// Copyright 2012-2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef, AttributePlace}; -use base; -use builder::{Builder, MemFlags}; -use common::{ty_fn_sig, C_usize}; -use context::CodegenCx; -use mir::place::PlaceRef; -use mir::operand::OperandValue; -use type_::Type; -use type_of::{LayoutLlvmExt, PointerKind}; - -use rustc_target::abi::{LayoutOf, Size, TyLayout}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout; - -use libc::c_uint; - -pub use rustc_target::spec::abi::Abi; -pub use rustc::ty::layout::{FAT_PTR_ADDR, FAT_PTR_EXTRA}; -pub use rustc_target::abi::call::*; - -macro_rules! for_each_kind { - ($flags: ident, $f: ident, $($kind: ident),+) => ({ - $(if $flags.contains(ArgAttribute::$kind) { $f(llvm::Attribute::$kind) })+ - }) -} - -trait ArgAttributeExt { - fn for_each_kind(&self, f: F) where F: FnMut(llvm::Attribute); -} - -impl ArgAttributeExt for ArgAttribute { - fn for_each_kind(&self, mut f: F) where F: FnMut(llvm::Attribute) { - for_each_kind!(self, f, - ByVal, NoAlias, NoCapture, NonNull, ReadOnly, SExt, StructRet, ZExt, InReg) - } -} - -pub trait ArgAttributesExt { - fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef); - fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef); -} - -impl ArgAttributesExt for ArgAttributes { - fn apply_llfn(&self, idx: AttributePlace, llfn: ValueRef) { - let mut regular = self.regular; - unsafe { - let deref = self.pointee_size.bytes(); - if deref != 0 { - if regular.contains(ArgAttribute::NonNull) { - llvm::LLVMRustAddDereferenceableAttr(llfn, - idx.as_uint(), - deref); - } else { - llvm::LLVMRustAddDereferenceableOrNullAttr(llfn, - idx.as_uint(), - deref); - } - regular -= ArgAttribute::NonNull; - } - if let Some(align) = self.pointee_align { - llvm::LLVMRustAddAlignmentAttr(llfn, - idx.as_uint(), - align.abi() as u32); - } - regular.for_each_kind(|attr| attr.apply_llfn(idx, llfn)); - } - } - - fn apply_callsite(&self, idx: AttributePlace, callsite: ValueRef) { - let mut regular = self.regular; - unsafe { - let deref = self.pointee_size.bytes(); - if deref != 0 { - if regular.contains(ArgAttribute::NonNull) { - llvm::LLVMRustAddDereferenceableCallSiteAttr(callsite, - idx.as_uint(), - deref); - } else { - llvm::LLVMRustAddDereferenceableOrNullCallSiteAttr(callsite, - idx.as_uint(), - deref); - } - regular -= ArgAttribute::NonNull; - } - if let Some(align) = self.pointee_align { - llvm::LLVMRustAddAlignmentCallSiteAttr(callsite, - idx.as_uint(), - align.abi() as u32); - } - regular.for_each_kind(|attr| attr.apply_callsite(idx, callsite)); - } - } -} - -pub trait LlvmType { - fn llvm_type(&self, cx: &CodegenCx) -> Type; -} - -impl LlvmType for Reg { - fn llvm_type(&self, cx: &CodegenCx) -> Type { - match self.kind { - RegKind::Integer => Type::ix(cx, self.size.bits()), - RegKind::Float => { - match self.size.bits() { - 32 => Type::f32(cx), - 64 => Type::f64(cx), - _ => bug!("unsupported float: {:?}", self) - } - } - RegKind::Vector => { - Type::vector(&Type::i8(cx), self.size.bytes()) - } - } - } -} - -impl LlvmType for CastTarget { - fn llvm_type(&self, cx: &CodegenCx) -> Type { - let rest_ll_unit = self.rest.unit.llvm_type(cx); - let rest_count = self.rest.total.bytes() / self.rest.unit.size.bytes(); - let rem_bytes = self.rest.total.bytes() % self.rest.unit.size.bytes(); - - if self.prefix.iter().all(|x| x.is_none()) { - // Simplify to a single unit when there is no prefix and size <= unit size - if self.rest.total <= self.rest.unit.size { - return rest_ll_unit; - } - - // Simplify to array when all chunks are the same size and type - if rem_bytes == 0 { - return Type::array(&rest_ll_unit, rest_count); - } - } - - // Create list of fields in the main structure - let mut args: Vec<_> = - self.prefix.iter().flat_map(|option_kind| option_kind.map( - |kind| Reg { kind: kind, size: self.prefix_chunk }.llvm_type(cx))) - .chain((0..rest_count).map(|_| rest_ll_unit)) - .collect(); - - // Append final integer - if rem_bytes != 0 { - // Only integers can be really split further. - assert_eq!(self.rest.unit.kind, RegKind::Integer); - args.push(Type::ix(cx, rem_bytes * 8)); - } - - Type::struct_(cx, &args, false) - } -} - -pub trait ArgTypeExt<'a, 'tcx> { - fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>); - fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>); -} - -impl<'a, 'tcx> ArgTypeExt<'a, 'tcx> for ArgType<'tcx, Ty<'tcx>> { - /// Get the LLVM type for a place of the original Rust type of - /// this argument/return, i.e. the result of `type_of::type_of`. - fn memory_ty(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - self.layout.llvm_type(cx) - } - - /// Store a direct/indirect value described by this ArgType into a - /// place for the original Rust type of this argument/return. - /// Can be used for both storing formal arguments into Rust variables - /// or results of call/invoke instructions into their destinations. - fn store(&self, bx: &Builder<'a, 'tcx>, val: ValueRef, dst: PlaceRef<'tcx>) { - if self.is_ignore() { - return; - } - let cx = bx.cx; - if self.is_indirect() { - OperandValue::Ref(val, self.layout.align).store(bx, dst) - } else if let PassMode::Cast(cast) = self.mode { - // FIXME(eddyb): Figure out when the simpler Store is safe, clang - // uses it for i16 -> {i8, i8}, but not for i24 -> {i8, i8, i8}. - let can_store_through_cast_ptr = false; - if can_store_through_cast_ptr { - let cast_dst = bx.pointercast(dst.llval, cast.llvm_type(cx).ptr_to()); - bx.store(val, cast_dst, self.layout.align); - } else { - // The actual return type is a struct, but the ABI - // adaptation code has cast it into some scalar type. The - // code that follows is the only reliable way I have - // found to do a transform like i64 -> {i32,i32}. - // Basically we dump the data onto the stack then memcpy it. - // - // Other approaches I tried: - // - Casting rust ret pointer to the foreign type and using Store - // is (a) unsafe if size of foreign type > size of rust type and - // (b) runs afoul of strict aliasing rules, yielding invalid - // assembly under -O (specifically, the store gets removed). - // - Truncating foreign type to correct integral type and then - // bitcasting to the struct type yields invalid cast errors. - - // We instead thus allocate some scratch space... - let scratch_size = cast.size(cx); - let scratch_align = cast.align(cx); - let llscratch = bx.alloca(cast.llvm_type(cx), "abi_cast", scratch_align); - bx.lifetime_start(llscratch, scratch_size); - - // ...where we first store the value... - bx.store(val, llscratch, scratch_align); - - // ...and then memcpy it to the intended destination. - base::call_memcpy(bx, - bx.pointercast(dst.llval, Type::i8p(cx)), - bx.pointercast(llscratch, Type::i8p(cx)), - C_usize(cx, self.layout.size.bytes()), - self.layout.align.min(scratch_align), - MemFlags::empty()); - - bx.lifetime_end(llscratch, scratch_size); - } - } else { - OperandValue::Immediate(val).store(bx, dst); - } - } - - fn store_fn_arg(&self, bx: &Builder<'a, 'tcx>, idx: &mut usize, dst: PlaceRef<'tcx>) { - let mut next = || { - let val = llvm::get_param(bx.llfn(), *idx as c_uint); - *idx += 1; - val - }; - match self.mode { - PassMode::Ignore => {}, - PassMode::Pair(..) => { - OperandValue::Pair(next(), next()).store(bx, dst); - } - PassMode::Direct(_) | PassMode::Indirect(_) | PassMode::Cast(_) => { - self.store(bx, next(), dst); - } - } - } -} - -pub trait FnTypeExt<'a, 'tcx> { - fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) - -> Self; - fn new(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn new_vtable(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn unadjusted(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self; - fn adjust_for_abi(&mut self, - cx: &CodegenCx<'a, 'tcx>, - abi: Abi); - fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn llvm_cconv(&self) -> llvm::CallConv; - fn apply_attrs_llfn(&self, llfn: ValueRef); - fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef); -} - -impl<'a, 'tcx> FnTypeExt<'a, 'tcx> for FnType<'tcx, Ty<'tcx>> { - fn of_instance(cx: &CodegenCx<'a, 'tcx>, instance: &ty::Instance<'tcx>) - -> Self { - let fn_ty = instance.ty(cx.tcx); - let sig = ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - FnType::new(cx, sig, &[]) - } - - fn new(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); - fn_ty.adjust_for_abi(cx, sig.abi); - fn_ty - } - - fn new_vtable(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - let mut fn_ty = FnType::unadjusted(cx, sig, extra_args); - // Don't pass the vtable, it's not an argument of the virtual fn. - { - let self_arg = &mut fn_ty.args[0]; - match self_arg.mode { - PassMode::Pair(data_ptr, _) => { - self_arg.mode = PassMode::Direct(data_ptr); - } - _ => bug!("FnType::new_vtable: non-pair self {:?}", self_arg) - } - - let pointee = self_arg.layout.ty.builtin_deref(true) - .unwrap_or_else(|| { - bug!("FnType::new_vtable: non-pointer self {:?}", self_arg) - }).ty; - let fat_ptr_ty = cx.tcx.mk_mut_ptr(pointee); - self_arg.layout = cx.layout_of(fat_ptr_ty).field(cx, 0); - } - fn_ty.adjust_for_abi(cx, sig.abi); - fn_ty - } - - fn unadjusted(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>, - extra_args: &[Ty<'tcx>]) -> Self { - debug!("FnType::unadjusted({:?}, {:?})", sig, extra_args); - - use self::Abi::*; - let conv = match cx.sess().target.target.adjust_abi(sig.abi) { - RustIntrinsic | PlatformIntrinsic | - Rust | RustCall => Conv::C, - - // It's the ABI's job to select this, not us. - System => bug!("system abi should be selected elsewhere"), - - Stdcall => Conv::X86Stdcall, - Fastcall => Conv::X86Fastcall, - Vectorcall => Conv::X86VectorCall, - Thiscall => Conv::X86ThisCall, - C => Conv::C, - Unadjusted => Conv::C, - Win64 => Conv::X86_64Win64, - SysV64 => Conv::X86_64SysV, - Aapcs => Conv::ArmAapcs, - PtxKernel => Conv::PtxKernel, - Msp430Interrupt => Conv::Msp430Intr, - X86Interrupt => Conv::X86Intr, - - // These API constants ought to be more specific... - Cdecl => Conv::C, - }; - - let mut inputs = sig.inputs(); - let extra_args = if sig.abi == RustCall { - assert!(!sig.variadic && extra_args.is_empty()); - - match sig.inputs().last().unwrap().sty { - ty::TyTuple(ref tupled_arguments) => { - inputs = &sig.inputs()[0..sig.inputs().len() - 1]; - tupled_arguments - } - _ => { - bug!("argument to function with \"rust-call\" ABI \ - is not a tuple"); - } - } - } else { - assert!(sig.variadic || extra_args.is_empty()); - extra_args - }; - - let target = &cx.sess().target.target; - let win_x64_gnu = target.target_os == "windows" - && target.arch == "x86_64" - && target.target_env == "gnu"; - let linux_s390x = target.target_os == "linux" - && target.arch == "s390x" - && target.target_env == "gnu"; - let rust_abi = match sig.abi { - RustIntrinsic | PlatformIntrinsic | Rust | RustCall => true, - _ => false - }; - - // Handle safe Rust thin and fat pointers. - let adjust_for_rust_scalar = |attrs: &mut ArgAttributes, - scalar: &layout::Scalar, - layout: TyLayout<'tcx, Ty<'tcx>>, - offset: Size, - is_return: bool| { - // Booleans are always an i1 that needs to be zero-extended. - if scalar.is_bool() { - attrs.set(ArgAttribute::ZExt); - return; - } - - // Only pointer types handled below. - if scalar.value != layout::Pointer { - return; - } - - if scalar.valid_range.start() < scalar.valid_range.end() { - if *scalar.valid_range.start() > 0 { - attrs.set(ArgAttribute::NonNull); - } - } - - if let Some(pointee) = layout.pointee_info_at(cx, offset) { - if let Some(kind) = pointee.safe { - attrs.pointee_size = pointee.size; - attrs.pointee_align = Some(pointee.align); - - // HACK(eddyb) LLVM inserts `llvm.assume` calls when inlining functions - // with align attributes, and those calls later block optimizations. - if !is_return && !cx.tcx.sess.opts.debugging_opts.arg_align_attributes { - attrs.pointee_align = None; - } - - // `Box` pointer parameters never alias because ownership is transferred - // `&mut` pointer parameters never alias other parameters, - // or mutable global data - // - // `&T` where `T` contains no `UnsafeCell` is immutable, - // and can be marked as both `readonly` and `noalias`, as - // LLVM's definition of `noalias` is based solely on memory - // dependencies rather than pointer equality - let no_alias = match kind { - PointerKind::Shared => false, - PointerKind::UniqueOwned => true, - PointerKind::Frozen | - PointerKind::UniqueBorrowed => !is_return - }; - if no_alias { - attrs.set(ArgAttribute::NoAlias); - } - - if kind == PointerKind::Frozen && !is_return { - attrs.set(ArgAttribute::ReadOnly); - } - } - } - }; - - let arg_of = |ty: Ty<'tcx>, is_return: bool| { - let mut arg = ArgType::new(cx.layout_of(ty)); - if arg.layout.is_zst() { - // For some forsaken reason, x86_64-pc-windows-gnu - // doesn't ignore zero-sized struct arguments. - // The same is true for s390x-unknown-linux-gnu. - if is_return || rust_abi || (!win_x64_gnu && !linux_s390x) { - arg.mode = PassMode::Ignore; - } - } - - // FIXME(eddyb) other ABIs don't have logic for scalar pairs. - if !is_return && rust_abi { - if let layout::Abi::ScalarPair(ref a, ref b) = arg.layout.abi { - let mut a_attrs = ArgAttributes::new(); - let mut b_attrs = ArgAttributes::new(); - adjust_for_rust_scalar(&mut a_attrs, - a, - arg.layout, - Size::from_bytes(0), - false); - adjust_for_rust_scalar(&mut b_attrs, - b, - arg.layout, - a.value.size(cx).abi_align(b.value.align(cx)), - false); - arg.mode = PassMode::Pair(a_attrs, b_attrs); - return arg; - } - } - - if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { - if let PassMode::Direct(ref mut attrs) = arg.mode { - adjust_for_rust_scalar(attrs, - scalar, - arg.layout, - Size::from_bytes(0), - is_return); - } - } - - arg - }; - - FnType { - ret: arg_of(sig.output(), true), - args: inputs.iter().chain(extra_args.iter()).map(|ty| { - arg_of(ty, false) - }).collect(), - variadic: sig.variadic, - conv, - } - } - - fn adjust_for_abi(&mut self, - cx: &CodegenCx<'a, 'tcx>, - abi: Abi) { - if abi == Abi::Unadjusted { return } - - if abi == Abi::Rust || abi == Abi::RustCall || - abi == Abi::RustIntrinsic || abi == Abi::PlatformIntrinsic { - let fixup = |arg: &mut ArgType<'tcx, Ty<'tcx>>| { - if arg.is_ignore() { return; } - - match arg.layout.abi { - layout::Abi::Aggregate { .. } => {} - - // This is a fun case! The gist of what this is doing is - // that we want callers and callees to always agree on the - // ABI of how they pass SIMD arguments. If we were to *not* - // make these arguments indirect then they'd be immediates - // in LLVM, which means that they'd used whatever the - // appropriate ABI is for the callee and the caller. That - // means, for example, if the caller doesn't have AVX - // enabled but the callee does, then passing an AVX argument - // across this boundary would cause corrupt data to show up. - // - // This problem is fixed by unconditionally passing SIMD - // arguments through memory between callers and callees - // which should get them all to agree on ABI regardless of - // target feature sets. Some more information about this - // issue can be found in #44367. - // - // Note that the platform intrinsic ABI is exempt here as - // that's how we connect up to LLVM and it's unstable - // anyway, we control all calls to it in libstd. - layout::Abi::Vector { .. } if abi != Abi::PlatformIntrinsic => { - arg.make_indirect(); - return - } - - _ => return - } - - let size = arg.layout.size; - if size > layout::Pointer.size(cx) { - arg.make_indirect(); - } else { - // We want to pass small aggregates as immediates, but using - // a LLVM aggregate type for this leads to bad optimizations, - // so we pick an appropriately sized integer type instead. - arg.cast_to(Reg { - kind: RegKind::Integer, - size - }); - } - }; - fixup(&mut self.ret); - for arg in &mut self.args { - fixup(arg); - } - if let PassMode::Indirect(ref mut attrs) = self.ret.mode { - attrs.set(ArgAttribute::StructRet); - } - return; - } - - if let Err(msg) = self.adjust_for_cabi(cx, abi) { - cx.sess().fatal(&msg); - } - } - - fn llvm_type(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - let mut llargument_tys = Vec::new(); - - let llreturn_ty = match self.ret.mode { - PassMode::Ignore => Type::void(cx), - PassMode::Direct(_) | PassMode::Pair(..) => { - self.ret.layout.immediate_llvm_type(cx) - } - PassMode::Cast(cast) => cast.llvm_type(cx), - PassMode::Indirect(_) => { - llargument_tys.push(self.ret.memory_ty(cx).ptr_to()); - Type::void(cx) - } - }; - - for arg in &self.args { - // add padding - if let Some(ty) = arg.pad { - llargument_tys.push(ty.llvm_type(cx)); - } - - let llarg_ty = match arg.mode { - PassMode::Ignore => continue, - PassMode::Direct(_) => arg.layout.immediate_llvm_type(cx), - PassMode::Pair(..) => { - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 0)); - llargument_tys.push(arg.layout.scalar_pair_element_llvm_type(cx, 1)); - continue; - } - PassMode::Cast(cast) => cast.llvm_type(cx), - PassMode::Indirect(_) => arg.memory_ty(cx).ptr_to(), - }; - llargument_tys.push(llarg_ty); - } - - if self.variadic { - Type::variadic_func(&llargument_tys, &llreturn_ty) - } else { - Type::func(&llargument_tys, &llreturn_ty) - } - } - - fn llvm_cconv(&self) -> llvm::CallConv { - match self.conv { - Conv::C => llvm::CCallConv, - Conv::ArmAapcs => llvm::ArmAapcsCallConv, - Conv::Msp430Intr => llvm::Msp430Intr, - Conv::PtxKernel => llvm::PtxKernel, - Conv::X86Fastcall => llvm::X86FastcallCallConv, - Conv::X86Intr => llvm::X86_Intr, - Conv::X86Stdcall => llvm::X86StdcallCallConv, - Conv::X86ThisCall => llvm::X86_ThisCall, - Conv::X86VectorCall => llvm::X86_VectorCall, - Conv::X86_64SysV => llvm::X86_64_SysV, - Conv::X86_64Win64 => llvm::X86_64_Win64, - } - } - - fn apply_attrs_llfn(&self, llfn: ValueRef) { - let mut i = 0; - let mut apply = |attrs: &ArgAttributes| { - attrs.apply_llfn(llvm::AttributePlace::Argument(i), llfn); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_llfn(llvm::AttributePlace::ReturnValue, llfn); - } - PassMode::Indirect(ref attrs) => apply(attrs), - _ => {} - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new()); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | - PassMode::Indirect(ref attrs) => apply(attrs), - PassMode::Pair(ref a, ref b) => { - apply(a); - apply(b); - } - PassMode::Cast(_) => apply(&ArgAttributes::new()), - } - } - } - - fn apply_attrs_callsite(&self, bx: &Builder<'a, 'tcx>, callsite: ValueRef) { - let mut i = 0; - let mut apply = |attrs: &ArgAttributes| { - attrs.apply_callsite(llvm::AttributePlace::Argument(i), callsite); - i += 1; - }; - match self.ret.mode { - PassMode::Direct(ref attrs) => { - attrs.apply_callsite(llvm::AttributePlace::ReturnValue, callsite); - } - PassMode::Indirect(ref attrs) => apply(attrs), - _ => {} - } - if let layout::Abi::Scalar(ref scalar) = self.ret.layout.abi { - // If the value is a boolean, the range is 0..2 and that ultimately - // become 0..0 when the type becomes i1, which would be rejected - // by the LLVM verifier. - match scalar.value { - layout::Int(..) if !scalar.is_bool() => { - let range = scalar.valid_range_exclusive(bx.cx); - if range.start != range.end { - // FIXME(nox): This causes very weird type errors about - // SHL operators in constants in stage 2 with LLVM 3.9. - if unsafe { llvm::LLVMRustVersionMajor() >= 4 } { - bx.range_metadata(callsite, range); - } - } - } - _ => {} - } - } - for arg in &self.args { - if arg.pad.is_some() { - apply(&ArgAttributes::new()); - } - match arg.mode { - PassMode::Ignore => {} - PassMode::Direct(ref attrs) | - PassMode::Indirect(ref attrs) => apply(attrs), - PassMode::Pair(ref a, ref b) => { - apply(a); - apply(b); - } - PassMode::Cast(_) => apply(&ArgAttributes::new()), - } - } - - let cconv = self.llvm_cconv(); - if cconv != llvm::CCallConv { - llvm::SetInstructionCallConv(callsite, cconv); - } - } -} diff --git a/src/librustc_trans/allocator.rs b/src/librustc_trans/allocator.rs deleted file mode 100644 index 871fe98ec01..00000000000 --- a/src/librustc_trans/allocator.rs +++ /dev/null @@ -1,103 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::ffi::CString; -use std::ptr; - -use attributes; -use libc::c_uint; -use rustc::middle::allocator::AllocatorKind; -use rustc::ty::TyCtxt; -use rustc_allocator::{ALLOCATOR_METHODS, AllocatorTy}; - -use ModuleLlvm; -use llvm::{self, False, True}; - -pub(crate) unsafe fn trans(tcx: TyCtxt, mods: &ModuleLlvm, kind: AllocatorKind) { - let llcx = mods.llcx; - let llmod = mods.llmod; - let usize = match &tcx.sess.target.target.target_pointer_width[..] { - "16" => llvm::LLVMInt16TypeInContext(llcx), - "32" => llvm::LLVMInt32TypeInContext(llcx), - "64" => llvm::LLVMInt64TypeInContext(llcx), - tws => bug!("Unsupported target word size for int: {}", tws), - }; - let i8 = llvm::LLVMInt8TypeInContext(llcx); - let i8p = llvm::LLVMPointerType(i8, 0); - let void = llvm::LLVMVoidTypeInContext(llcx); - - for method in ALLOCATOR_METHODS { - let mut args = Vec::new(); - for ty in method.inputs.iter() { - match *ty { - AllocatorTy::Layout => { - args.push(usize); // size - args.push(usize); // align - } - AllocatorTy::Ptr => args.push(i8p), - AllocatorTy::Usize => args.push(usize), - - AllocatorTy::ResultPtr | - AllocatorTy::Unit => panic!("invalid allocator arg"), - } - } - let output = match method.output { - AllocatorTy::ResultPtr => Some(i8p), - AllocatorTy::Unit => None, - - AllocatorTy::Layout | - AllocatorTy::Usize | - AllocatorTy::Ptr => panic!("invalid allocator output"), - }; - let ty = llvm::LLVMFunctionType(output.unwrap_or(void), - args.as_ptr(), - args.len() as c_uint, - False); - let name = CString::new(format!("__rust_{}", method.name)).unwrap(); - let llfn = llvm::LLVMRustGetOrInsertFunction(llmod, - name.as_ptr(), - ty); - - if tcx.sess.target.target.options.default_hidden_visibility { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - if tcx.sess.target.target.options.requires_uwtable { - attributes::emit_uwtable(llfn, true); - } - - let callee = CString::new(kind.fn_name(method.name)).unwrap(); - let callee = llvm::LLVMRustGetOrInsertFunction(llmod, - callee.as_ptr(), - ty); - - let llbb = llvm::LLVMAppendBasicBlockInContext(llcx, - llfn, - "entry\0".as_ptr() as *const _); - - let llbuilder = llvm::LLVMCreateBuilderInContext(llcx); - llvm::LLVMPositionBuilderAtEnd(llbuilder, llbb); - let args = args.iter().enumerate().map(|(i, _)| { - llvm::LLVMGetParam(llfn, i as c_uint) - }).collect::>(); - let ret = llvm::LLVMRustBuildCall(llbuilder, - callee, - args.as_ptr(), - args.len() as c_uint, - ptr::null_mut(), - "\0".as_ptr() as *const _); - llvm::LLVMSetTailCall(ret, True); - if output.is_some() { - llvm::LLVMBuildRet(llbuilder, ret); - } else { - llvm::LLVMBuildRetVoid(llbuilder); - } - llvm::LLVMDisposeBuilder(llbuilder); - } -} diff --git a/src/librustc_trans/asm.rs b/src/librustc_trans/asm.rs deleted file mode 100644 index 751f8148a2a..00000000000 --- a/src/librustc_trans/asm.rs +++ /dev/null @@ -1,129 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Translation of inline assembly. - -use llvm::{self, ValueRef}; -use common::*; -use type_::Type; -use type_of::LayoutLlvmExt; -use builder::Builder; - -use rustc::hir; - -use mir::place::PlaceRef; -use mir::operand::OperandValue; - -use std::ffi::CString; -use syntax::ast::AsmDialect; -use libc::{c_uint, c_char}; - -// Take an inline assembly expression and splat it out via LLVM -pub fn trans_inline_asm<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - ia: &hir::InlineAsm, - outputs: Vec>, - mut inputs: Vec -) { - let mut ext_constraints = vec![]; - let mut output_types = vec![]; - - // Prepare the output operands - let mut indirect_outputs = vec![]; - for (i, (out, place)) in ia.outputs.iter().zip(&outputs).enumerate() { - if out.is_rw { - inputs.push(place.load(bx).immediate()); - ext_constraints.push(i.to_string()); - } - if out.is_indirect { - indirect_outputs.push(place.load(bx).immediate()); - } else { - output_types.push(place.layout.llvm_type(bx.cx)); - } - } - if !indirect_outputs.is_empty() { - indirect_outputs.extend_from_slice(&inputs); - inputs = indirect_outputs; - } - - let clobbers = ia.clobbers.iter() - .map(|s| format!("~{{{}}}", &s)); - - // Default per-arch clobbers - // Basically what clang does - let arch_clobbers = match &bx.sess().target.target.arch[..] { - "x86" | "x86_64" => vec!["~{dirflag}", "~{fpsr}", "~{flags}"], - "mips" | "mips64" => vec!["~{$1}"], - _ => Vec::new() - }; - - let all_constraints = - ia.outputs.iter().map(|out| out.constraint.to_string()) - .chain(ia.inputs.iter().map(|s| s.to_string())) - .chain(ext_constraints) - .chain(clobbers) - .chain(arch_clobbers.iter().map(|s| s.to_string())) - .collect::>().join(","); - - debug!("Asm Constraints: {}", &all_constraints); - - // Depending on how many outputs we have, the return type is different - let num_outputs = output_types.len(); - let output_type = match num_outputs { - 0 => Type::void(bx.cx), - 1 => output_types[0], - _ => Type::struct_(bx.cx, &output_types, false) - }; - - let dialect = match ia.dialect { - AsmDialect::Att => llvm::AsmDialect::Att, - AsmDialect::Intel => llvm::AsmDialect::Intel, - }; - - let asm = CString::new(ia.asm.as_str().as_bytes()).unwrap(); - let constraint_cstr = CString::new(all_constraints).unwrap(); - let r = bx.inline_asm_call( - asm.as_ptr(), - constraint_cstr.as_ptr(), - &inputs, - output_type, - ia.volatile, - ia.alignstack, - dialect - ); - - // Again, based on how many outputs we have - let outputs = ia.outputs.iter().zip(&outputs).filter(|&(ref o, _)| !o.is_indirect); - for (i, (_, &place)) in outputs.enumerate() { - let v = if num_outputs == 1 { r } else { bx.extract_value(r, i as u64) }; - OperandValue::Immediate(v).store(bx, place); - } - - // Store mark in a metadata node so we can map LLVM errors - // back to source locations. See #17552. - unsafe { - let key = "srcloc"; - let kind = llvm::LLVMGetMDKindIDInContext(bx.cx.llcx, - key.as_ptr() as *const c_char, key.len() as c_uint); - - let val: llvm::ValueRef = C_i32(bx.cx, ia.ctxt.outer().as_u32() as i32); - - llvm::LLVMSetMetadata(r, kind, - llvm::LLVMMDNodeInContext(bx.cx.llcx, &val, 1)); - } -} - -pub fn trans_global_asm<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ga: &hir::GlobalAsm) { - let asm = CString::new(ga.asm.as_str().as_bytes()).unwrap(); - unsafe { - llvm::LLVMRustAppendModuleInlineAsm(cx.llmod, asm.as_ptr()); - } -} diff --git a/src/librustc_trans/attributes.rs b/src/librustc_trans/attributes.rs deleted file mode 100644 index 5baed57092d..00000000000 --- a/src/librustc_trans/attributes.rs +++ /dev/null @@ -1,259 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -//! Set and unset common attributes on LLVM values. - -use std::ffi::{CStr, CString}; - -use rustc::hir::{self, TransFnAttrFlags}; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use rustc::hir::itemlikevisit::ItemLikeVisitor; -use rustc::session::Session; -use rustc::session::config::Sanitizer; -use rustc::ty::TyCtxt; -use rustc::ty::maps::Providers; -use rustc_data_structures::sync::Lrc; -use rustc_data_structures::fx::FxHashMap; - -use llvm::{self, Attribute, ValueRef}; -use llvm::AttributePlace::Function; -use llvm_util; -pub use syntax::attr::{self, InlineAttr}; -use context::CodegenCx; - -/// Mark LLVM function to use provided inline heuristic. -#[inline] -pub fn inline(val: ValueRef, inline: InlineAttr) { - use self::InlineAttr::*; - match inline { - Hint => Attribute::InlineHint.apply_llfn(Function, val), - Always => Attribute::AlwaysInline.apply_llfn(Function, val), - Never => Attribute::NoInline.apply_llfn(Function, val), - None => { - Attribute::InlineHint.unapply_llfn(Function, val); - Attribute::AlwaysInline.unapply_llfn(Function, val); - Attribute::NoInline.unapply_llfn(Function, val); - }, - }; -} - -/// Tell LLVM to emit or not emit the information necessary to unwind the stack for the function. -#[inline] -pub fn emit_uwtable(val: ValueRef, emit: bool) { - Attribute::UWTable.toggle_llfn(Function, val, emit); -} - -/// Tell LLVM whether the function can or cannot unwind. -#[inline] -pub fn unwind(val: ValueRef, can_unwind: bool) { - Attribute::NoUnwind.toggle_llfn(Function, val, !can_unwind); -} - -/// Tell LLVM whether it should optimize function for size. -#[inline] -#[allow(dead_code)] // possibly useful function -pub fn set_optimize_for_size(val: ValueRef, optimize: bool) { - Attribute::OptimizeForSize.toggle_llfn(Function, val, optimize); -} - -/// Tell LLVM if this function should be 'naked', i.e. skip the epilogue and prologue. -#[inline] -pub fn naked(val: ValueRef, is_naked: bool) { - Attribute::Naked.toggle_llfn(Function, val, is_naked); -} - -pub fn set_frame_pointer_elimination(cx: &CodegenCx, llfn: ValueRef) { - if cx.sess().must_not_eliminate_frame_pointers() { - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("no-frame-pointer-elim\0"), cstr("true\0")); - } -} - -pub fn set_probestack(cx: &CodegenCx, llfn: ValueRef) { - // Only use stack probes if the target specification indicates that we - // should be using stack probes - if !cx.sess().target.target.options.stack_probes { - return - } - - // Currently stack probes seem somewhat incompatible with the address - // sanitizer. With asan we're already protected from stack overflow anyway - // so we don't really need stack probes regardless. - match cx.sess().opts.debugging_opts.sanitizer { - Some(Sanitizer::Address) => return, - _ => {} - } - - // probestack doesn't play nice either with pgo-gen. - if cx.sess().opts.debugging_opts.pgo_gen.is_some() { - return; - } - - // Flag our internal `__rust_probestack` function as the stack probe symbol. - // This is defined in the `compiler-builtins` crate for each architecture. - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("probe-stack\0"), cstr("__rust_probestack\0")); -} - -pub fn llvm_target_features(sess: &Session) -> impl Iterator { - const RUSTC_SPECIFIC_FEATURES: &[&str] = &[ - "crt-static", - ]; - - let cmdline = sess.opts.cg.target_feature.split(',') - .filter(|f| !RUSTC_SPECIFIC_FEATURES.iter().any(|s| f.contains(s))); - sess.target.target.options.features.split(',') - .chain(cmdline) - .filter(|l| !l.is_empty()) -} - -/// Composite function which sets LLVM attributes for function depending on its AST (#[attribute]) -/// attributes. -pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { - let trans_fn_attrs = cx.tcx.trans_fn_attrs(id); - - inline(llfn, trans_fn_attrs.inline); - - set_frame_pointer_elimination(cx, llfn); - set_probestack(cx, llfn); - - if trans_fn_attrs.flags.contains(TransFnAttrFlags::COLD) { - Attribute::Cold.apply_llfn(Function, llfn); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::NAKED) { - naked(llfn, true); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::ALLOCATOR) { - Attribute::NoAlias.apply_llfn( - llvm::AttributePlace::ReturnValue, llfn); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::UNWIND) { - unwind(llfn, true); - } - if trans_fn_attrs.flags.contains(TransFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - unwind(llfn, false); - } - - let features = llvm_target_features(cx.tcx.sess) - .map(|s| s.to_string()) - .chain( - trans_fn_attrs.target_features - .iter() - .map(|f| { - let feature = &*f.as_str(); - format!("+{}", llvm_util::to_llvm_feature(cx.tcx.sess, feature)) - }) - ) - .collect::>() - .join(","); - - if !features.is_empty() { - let val = CString::new(features).unwrap(); - llvm::AddFunctionAttrStringValue( - llfn, llvm::AttributePlace::Function, - cstr("target-features\0"), &val); - } - - // Note that currently the `wasm-import-module` doesn't do anything, but - // eventually LLVM 7 should read this and ferry the appropriate import - // module to the output file. - if cx.tcx.sess.target.target.arch == "wasm32" { - if let Some(module) = wasm_import_module(cx.tcx, id) { - llvm::AddFunctionAttrStringValue( - llfn, - llvm::AttributePlace::Function, - cstr("wasm-import-module\0"), - &module, - ); - } - } -} - -fn cstr(s: &'static str) -> &CStr { - CStr::from_bytes_with_nul(s.as_bytes()).expect("null-terminated string") -} - -pub fn provide(providers: &mut Providers) { - providers.target_features_whitelist = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - if tcx.sess.opts.actually_rustdoc { - // rustdoc needs to be able to document functions that use all the features, so - // whitelist them all - Lrc::new(llvm_util::all_known_features() - .map(|(a, b)| (a.to_string(), b.map(|s| s.to_string()))) - .collect()) - } else { - Lrc::new(llvm_util::target_feature_whitelist(tcx.sess) - .iter() - .map(|&(a, b)| (a.to_string(), b.map(|s| s.to_string()))) - .collect()) - } - }; - - providers.wasm_custom_sections = |tcx, cnum| { - assert_eq!(cnum, LOCAL_CRATE); - let mut finder = WasmSectionFinder { tcx, list: Vec::new() }; - tcx.hir.krate().visit_all_item_likes(&mut finder); - Lrc::new(finder.list) - }; - - provide_extern(providers); -} - -struct WasmSectionFinder<'a, 'tcx: 'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, - list: Vec, -} - -impl<'a, 'tcx: 'a> ItemLikeVisitor<'tcx> for WasmSectionFinder<'a, 'tcx> { - fn visit_item(&mut self, i: &'tcx hir::Item) { - match i.node { - hir::ItemConst(..) => {} - _ => return, - } - if i.attrs.iter().any(|i| i.check_name("wasm_custom_section")) { - self.list.push(self.tcx.hir.local_def_id(i.id)); - } - } - - fn visit_trait_item(&mut self, _: &'tcx hir::TraitItem) {} - - fn visit_impl_item(&mut self, _: &'tcx hir::ImplItem) {} -} - -pub fn provide_extern(providers: &mut Providers) { - providers.wasm_import_module_map = |tcx, cnum| { - let mut ret = FxHashMap(); - for lib in tcx.foreign_modules(cnum).iter() { - let attrs = tcx.get_attrs(lib.def_id); - let mut module = None; - for attr in attrs.iter().filter(|a| a.check_name("wasm_import_module")) { - module = attr.value_str(); - } - let module = match module { - Some(s) => s, - None => continue, - }; - for id in lib.foreign_items.iter() { - assert_eq!(id.krate, cnum); - ret.insert(*id, module.to_string()); - } - } - - Lrc::new(ret) - } -} - -fn wasm_import_module(tcx: TyCtxt, id: DefId) -> Option { - tcx.wasm_import_module_map(id.krate) - .get(&id) - .map(|s| CString::new(&s[..]).unwrap()) -} diff --git a/src/librustc_trans/back/archive.rs b/src/librustc_trans/back/archive.rs deleted file mode 100644 index 609629bffb9..00000000000 --- a/src/librustc_trans/back/archive.rs +++ /dev/null @@ -1,325 +0,0 @@ -// Copyright 2013-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A helper class for dealing with static archives - -use std::ffi::{CString, CStr}; -use std::io; -use std::mem; -use std::path::{Path, PathBuf}; -use std::ptr; -use std::str; - -use back::bytecode::RLIB_BYTECODE_EXTENSION; -use libc; -use llvm::archive_ro::{ArchiveRO, Child}; -use llvm::{self, ArchiveKind}; -use metadata::METADATA_FILENAME; -use rustc::session::Session; - -pub struct ArchiveConfig<'a> { - pub sess: &'a Session, - pub dst: PathBuf, - pub src: Option, - pub lib_search_paths: Vec, -} - -/// Helper for adding many files to an archive. -#[must_use = "must call build() to finish building the archive"] -pub struct ArchiveBuilder<'a> { - config: ArchiveConfig<'a>, - removals: Vec, - additions: Vec, - should_update_symbols: bool, - src_archive: Option>, -} - -enum Addition { - File { - path: PathBuf, - name_in_archive: String, - }, - Archive { - archive: ArchiveRO, - skip: Box bool>, - }, -} - -pub fn find_library(name: &str, search_paths: &[PathBuf], sess: &Session) - -> PathBuf { - // On Windows, static libraries sometimes show up as libfoo.a and other - // times show up as foo.lib - let oslibname = format!("{}{}{}", - sess.target.target.options.staticlib_prefix, - name, - sess.target.target.options.staticlib_suffix); - let unixlibname = format!("lib{}.a", name); - - for path in search_paths { - debug!("looking for {} inside {:?}", name, path); - let test = path.join(&oslibname); - if test.exists() { return test } - if oslibname != unixlibname { - let test = path.join(&unixlibname); - if test.exists() { return test } - } - } - sess.fatal(&format!("could not find native static library `{}`, \ - perhaps an -L flag is missing?", name)); -} - -fn is_relevant_child(c: &Child) -> bool { - match c.name() { - Some(name) => !name.contains("SYMDEF"), - None => false, - } -} - -impl<'a> ArchiveBuilder<'a> { - /// Create a new static archive, ready for modifying the archive specified - /// by `config`. - pub fn new(config: ArchiveConfig<'a>) -> ArchiveBuilder<'a> { - ArchiveBuilder { - config, - removals: Vec::new(), - additions: Vec::new(), - should_update_symbols: false, - src_archive: None, - } - } - - /// Removes a file from this archive - pub fn remove_file(&mut self, file: &str) { - self.removals.push(file.to_string()); - } - - /// Lists all files in an archive - pub fn src_files(&mut self) -> Vec { - if self.src_archive().is_none() { - return Vec::new() - } - let archive = self.src_archive.as_ref().unwrap().as_ref().unwrap(); - let ret = archive.iter() - .filter_map(|child| child.ok()) - .filter(is_relevant_child) - .filter_map(|child| child.name()) - .filter(|name| !self.removals.iter().any(|x| x == name)) - .map(|name| name.to_string()) - .collect(); - return ret; - } - - fn src_archive(&mut self) -> Option<&ArchiveRO> { - if let Some(ref a) = self.src_archive { - return a.as_ref() - } - let src = self.config.src.as_ref()?; - self.src_archive = Some(ArchiveRO::open(src).ok()); - self.src_archive.as_ref().unwrap().as_ref() - } - - /// Adds all of the contents of a native library to this archive. This will - /// search in the relevant locations for a library named `name`. - pub fn add_native_library(&mut self, name: &str) { - let location = find_library(name, &self.config.lib_search_paths, - self.config.sess); - self.add_archive(&location, |_| false).unwrap_or_else(|e| { - self.config.sess.fatal(&format!("failed to add native library {}: {}", - location.to_string_lossy(), e)); - }); - } - - /// Adds all of the contents of the rlib at the specified path to this - /// archive. - /// - /// This ignores adding the bytecode from the rlib, and if LTO is enabled - /// then the object file also isn't added. - pub fn add_rlib(&mut self, - rlib: &Path, - name: &str, - lto: bool, - skip_objects: bool) -> io::Result<()> { - // Ignoring obj file starting with the crate name - // as simple comparison is not enough - there - // might be also an extra name suffix - let obj_start = format!("{}", name); - - self.add_archive(rlib, move |fname: &str| { - // Ignore bytecode/metadata files, no matter the name. - if fname.ends_with(RLIB_BYTECODE_EXTENSION) || fname == METADATA_FILENAME { - return true - } - - // Don't include Rust objects if LTO is enabled - if lto && fname.starts_with(&obj_start) && fname.ends_with(".o") { - return true - } - - // Otherwise if this is *not* a rust object and we're skipping - // objects then skip this file - if skip_objects && (!fname.starts_with(&obj_start) || !fname.ends_with(".o")) { - return true - } - - // ok, don't skip this - return false - }) - } - - fn add_archive(&mut self, archive: &Path, skip: F) - -> io::Result<()> - where F: FnMut(&str) -> bool + 'static - { - let archive = match ArchiveRO::open(archive) { - Ok(ar) => ar, - Err(e) => return Err(io::Error::new(io::ErrorKind::Other, e)), - }; - self.additions.push(Addition::Archive { - archive, - skip: Box::new(skip), - }); - Ok(()) - } - - /// Adds an arbitrary file to this archive - pub fn add_file(&mut self, file: &Path) { - let name = file.file_name().unwrap().to_str().unwrap(); - self.additions.push(Addition::File { - path: file.to_path_buf(), - name_in_archive: name.to_string(), - }); - } - - /// Indicate that the next call to `build` should update all symbols in - /// the archive (equivalent to running 'ar s' over it). - pub fn update_symbols(&mut self) { - self.should_update_symbols = true; - } - - /// Combine the provided files, rlibs, and native libraries into a single - /// `Archive`. - pub fn build(&mut self) { - let kind = match self.llvm_archive_kind() { - Ok(kind) => kind, - Err(kind) => { - self.config.sess.fatal(&format!("Don't know how to build archive of type: {}", - kind)); - } - }; - - if let Err(e) = self.build_with_llvm(kind) { - self.config.sess.fatal(&format!("failed to build archive: {}", e)); - } - - } - - fn llvm_archive_kind(&self) -> Result { - let kind = &*self.config.sess.target.target.options.archive_format; - kind.parse().map_err(|_| kind) - } - - fn build_with_llvm(&mut self, kind: ArchiveKind) -> io::Result<()> { - let mut archives = Vec::new(); - let mut strings = Vec::new(); - let mut members = Vec::new(); - let removals = mem::replace(&mut self.removals, Vec::new()); - - unsafe { - if let Some(archive) = self.src_archive() { - for child in archive.iter() { - let child = child.map_err(string_to_io_error)?; - let child_name = match child.name() { - Some(s) => s, - None => continue, - }; - if removals.iter().any(|r| r == child_name) { - continue - } - - let name = CString::new(child_name)?; - members.push(llvm::LLVMRustArchiveMemberNew(ptr::null(), - name.as_ptr(), - child.raw())); - strings.push(name); - } - } - for addition in mem::replace(&mut self.additions, Vec::new()) { - match addition { - Addition::File { path, name_in_archive } => { - let path = CString::new(path.to_str().unwrap())?; - let name = CString::new(name_in_archive)?; - members.push(llvm::LLVMRustArchiveMemberNew(path.as_ptr(), - name.as_ptr(), - ptr::null_mut())); - strings.push(path); - strings.push(name); - } - Addition::Archive { archive, mut skip } => { - for child in archive.iter() { - let child = child.map_err(string_to_io_error)?; - if !is_relevant_child(&child) { - continue - } - let child_name = child.name().unwrap(); - if skip(child_name) { - continue - } - - // It appears that LLVM's archive writer is a little - // buggy if the name we pass down isn't just the - // filename component, so chop that off here and - // pass it in. - // - // See LLVM bug 25877 for more info. - let child_name = Path::new(child_name) - .file_name().unwrap() - .to_str().unwrap(); - let name = CString::new(child_name)?; - let m = llvm::LLVMRustArchiveMemberNew(ptr::null(), - name.as_ptr(), - child.raw()); - members.push(m); - strings.push(name); - } - archives.push(archive); - } - } - } - - let dst = self.config.dst.to_str().unwrap().as_bytes(); - let dst = CString::new(dst)?; - let r = llvm::LLVMRustWriteArchive(dst.as_ptr(), - members.len() as libc::size_t, - members.as_ptr(), - self.should_update_symbols, - kind); - let ret = if r.into_result().is_err() { - let err = llvm::LLVMRustGetLastError(); - let msg = if err.is_null() { - "failed to write archive".to_string() - } else { - String::from_utf8_lossy(CStr::from_ptr(err).to_bytes()) - .into_owned() - }; - Err(io::Error::new(io::ErrorKind::Other, msg)) - } else { - Ok(()) - }; - for member in members { - llvm::LLVMRustArchiveMemberFree(member); - } - return ret - } - } -} - -fn string_to_io_error(s: String) -> io::Error { - io::Error::new(io::ErrorKind::Other, format!("bad archive: {}", s)) -} diff --git a/src/librustc_trans/back/bytecode.rs b/src/librustc_trans/back/bytecode.rs deleted file mode 100644 index 212d1aaf055..00000000000 --- a/src/librustc_trans/back/bytecode.rs +++ /dev/null @@ -1,160 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Management of the encoding of LLVM bytecode into rlibs -//! -//! This module contains the management of encoding LLVM bytecode into rlibs, -//! primarily for the usage in LTO situations. Currently the compiler will -//! unconditionally encode LLVM-IR into rlibs regardless of what's happening -//! elsewhere, so we currently compress the bytecode via deflate to avoid taking -//! up too much space on disk. -//! -//! After compressing the bytecode we then have the rest of the format to -//! basically deal with various bugs in various archive implementations. The -//! format currently is: -//! -//! RLIB LLVM-BYTECODE OBJECT LAYOUT -//! Version 2 -//! Bytes Data -//! 0..10 "RUST_OBJECT" encoded in ASCII -//! 11..14 format version as little-endian u32 -//! 15..19 the length of the module identifier string -//! 20..n the module identifier string -//! n..n+8 size in bytes of deflate compressed LLVM bitcode as -//! little-endian u64 -//! n+9.. compressed LLVM bitcode -//! ? maybe a byte to make this whole thing even length - -use std::io::{Read, Write}; -use std::ptr; -use std::str; - -use flate2::Compression; -use flate2::read::DeflateDecoder; -use flate2::write::DeflateEncoder; - -// This is the "magic number" expected at the beginning of a LLVM bytecode -// object in an rlib. -pub const RLIB_BYTECODE_OBJECT_MAGIC: &'static [u8] = b"RUST_OBJECT"; - -// The version number this compiler will write to bytecode objects in rlibs -pub const RLIB_BYTECODE_OBJECT_VERSION: u8 = 2; - -pub const RLIB_BYTECODE_EXTENSION: &str = "bc.z"; - -pub fn encode(identifier: &str, bytecode: &[u8]) -> Vec { - let mut encoded = Vec::new(); - - // Start off with the magic string - encoded.extend_from_slice(RLIB_BYTECODE_OBJECT_MAGIC); - - // Next up is the version - encoded.extend_from_slice(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]); - - // Next is the LLVM module identifier length + contents - let identifier_len = identifier.len(); - encoded.extend_from_slice(&[ - (identifier_len >> 0) as u8, - (identifier_len >> 8) as u8, - (identifier_len >> 16) as u8, - (identifier_len >> 24) as u8, - ]); - encoded.extend_from_slice(identifier.as_bytes()); - - // Next is the LLVM module deflate compressed, prefixed with its length. We - // don't know its length yet, so fill in 0s - let deflated_size_pos = encoded.len(); - encoded.extend_from_slice(&[0, 0, 0, 0, 0, 0, 0, 0]); - - let before = encoded.len(); - DeflateEncoder::new(&mut encoded, Compression::fast()) - .write_all(bytecode) - .unwrap(); - let after = encoded.len(); - - // Fill in the length we reserved space for before - let bytecode_len = (after - before) as u64; - encoded[deflated_size_pos + 0] = (bytecode_len >> 0) as u8; - encoded[deflated_size_pos + 1] = (bytecode_len >> 8) as u8; - encoded[deflated_size_pos + 2] = (bytecode_len >> 16) as u8; - encoded[deflated_size_pos + 3] = (bytecode_len >> 24) as u8; - encoded[deflated_size_pos + 4] = (bytecode_len >> 32) as u8; - encoded[deflated_size_pos + 5] = (bytecode_len >> 40) as u8; - encoded[deflated_size_pos + 6] = (bytecode_len >> 48) as u8; - encoded[deflated_size_pos + 7] = (bytecode_len >> 56) as u8; - - // If the number of bytes written to the object so far is odd, add a - // padding byte to make it even. This works around a crash bug in LLDB - // (see issue #15950) - if encoded.len() % 2 == 1 { - encoded.push(0); - } - - return encoded -} - -pub struct DecodedBytecode<'a> { - identifier: &'a str, - encoded_bytecode: &'a [u8], -} - -impl<'a> DecodedBytecode<'a> { - pub fn new(data: &'a [u8]) -> Result, String> { - if !data.starts_with(RLIB_BYTECODE_OBJECT_MAGIC) { - return Err(format!("magic bytecode prefix not found")) - } - let data = &data[RLIB_BYTECODE_OBJECT_MAGIC.len()..]; - if !data.starts_with(&[RLIB_BYTECODE_OBJECT_VERSION, 0, 0, 0]) { - return Err(format!("wrong version prefix found in bytecode")) - } - let data = &data[4..]; - if data.len() < 4 { - return Err(format!("bytecode corrupted")) - } - let identifier_len = unsafe { - u32::from_le(ptr::read_unaligned(data.as_ptr() as *const u32)) as usize - }; - let data = &data[4..]; - if data.len() < identifier_len { - return Err(format!("bytecode corrupted")) - } - let identifier = match str::from_utf8(&data[..identifier_len]) { - Ok(s) => s, - Err(_) => return Err(format!("bytecode corrupted")) - }; - let data = &data[identifier_len..]; - if data.len() < 8 { - return Err(format!("bytecode corrupted")) - } - let bytecode_len = unsafe { - u64::from_le(ptr::read_unaligned(data.as_ptr() as *const u64)) as usize - }; - let data = &data[8..]; - if data.len() < bytecode_len { - return Err(format!("bytecode corrupted")) - } - let encoded_bytecode = &data[..bytecode_len]; - - Ok(DecodedBytecode { - identifier, - encoded_bytecode, - }) - } - - pub fn bytecode(&self) -> Vec { - let mut data = Vec::new(); - DeflateDecoder::new(self.encoded_bytecode).read_to_end(&mut data).unwrap(); - return data - } - - pub fn identifier(&self) -> &'a str { - self.identifier - } -} diff --git a/src/librustc_trans/back/command.rs b/src/librustc_trans/back/command.rs deleted file mode 100644 index 9ebbdd7c3c9..00000000000 --- a/src/librustc_trans/back/command.rs +++ /dev/null @@ -1,175 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A thin wrapper around `Command` in the standard library which allows us to -//! read the arguments that are built up. - -use std::ffi::{OsStr, OsString}; -use std::fmt; -use std::io; -use std::mem; -use std::process::{self, Output}; - -use rustc_target::spec::LldFlavor; - -#[derive(Clone)] -pub struct Command { - program: Program, - args: Vec, - env: Vec<(OsString, OsString)>, -} - -#[derive(Clone)] -enum Program { - Normal(OsString), - CmdBatScript(OsString), - Lld(OsString, LldFlavor) -} - -impl Command { - pub fn new>(program: P) -> Command { - Command::_new(Program::Normal(program.as_ref().to_owned())) - } - - pub fn bat_script>(program: P) -> Command { - Command::_new(Program::CmdBatScript(program.as_ref().to_owned())) - } - - pub fn lld>(program: P, flavor: LldFlavor) -> Command { - Command::_new(Program::Lld(program.as_ref().to_owned(), flavor)) - } - - fn _new(program: Program) -> Command { - Command { - program, - args: Vec::new(), - env: Vec::new(), - } - } - - pub fn arg>(&mut self, arg: P) -> &mut Command { - self._arg(arg.as_ref()); - self - } - - pub fn args(&mut self, args: I) -> &mut Command - where I: IntoIterator, - I::Item: AsRef, - { - for arg in args { - self._arg(arg.as_ref()); - } - self - } - - fn _arg(&mut self, arg: &OsStr) { - self.args.push(arg.to_owned()); - } - - pub fn env(&mut self, key: K, value: V) -> &mut Command - where K: AsRef, - V: AsRef - { - self._env(key.as_ref(), value.as_ref()); - self - } - - fn _env(&mut self, key: &OsStr, value: &OsStr) { - self.env.push((key.to_owned(), value.to_owned())); - } - - pub fn output(&mut self) -> io::Result { - self.command().output() - } - - pub fn command(&self) -> process::Command { - let mut ret = match self.program { - Program::Normal(ref p) => process::Command::new(p), - Program::CmdBatScript(ref p) => { - let mut c = process::Command::new("cmd"); - c.arg("/c").arg(p); - c - } - Program::Lld(ref p, flavor) => { - let mut c = process::Command::new(p); - c.arg("-flavor").arg(match flavor { - LldFlavor::Wasm => "wasm", - LldFlavor::Ld => "gnu", - LldFlavor::Link => "link", - LldFlavor::Ld64 => "darwin", - }); - c - } - }; - ret.args(&self.args); - ret.envs(self.env.clone()); - return ret - } - - // extensions - - pub fn get_args(&self) -> &[OsString] { - &self.args - } - - pub fn take_args(&mut self) -> Vec { - mem::replace(&mut self.args, Vec::new()) - } - - /// Returns a `true` if we're pretty sure that this'll blow OS spawn limits, - /// or `false` if we should attempt to spawn and see what the OS says. - pub fn very_likely_to_exceed_some_spawn_limit(&self) -> bool { - // We mostly only care about Windows in this method, on Unix the limits - // can be gargantuan anyway so we're pretty unlikely to hit them - if cfg!(unix) { - return false - } - - // Right now LLD doesn't support the `@` syntax of passing an argument - // through files, so regardless of the platform we try to go to the OS - // on this one. - if let Program::Lld(..) = self.program { - return false - } - - // Ok so on Windows to spawn a process is 32,768 characters in its - // command line [1]. Unfortunately we don't actually have access to that - // as it's calculated just before spawning. Instead we perform a - // poor-man's guess as to how long our command line will be. We're - // assuming here that we don't have to escape every character... - // - // Turns out though that `cmd.exe` has even smaller limits, 8192 - // characters [2]. Linkers can often be batch scripts (for example - // Emscripten, Gecko's current build system) which means that we're - // running through batch scripts. These linkers often just forward - // arguments elsewhere (and maybe tack on more), so if we blow 8192 - // bytes we'll typically cause them to blow as well. - // - // Basically as a result just perform an inflated estimate of what our - // command line will look like and test if it's > 8192 (we actually - // test against 6k to artificially inflate our estimate). If all else - // fails we'll fall back to the normal unix logic of testing the OS - // error code if we fail to spawn and automatically re-spawning the - // linker with smaller arguments. - // - // [1]: https://msdn.microsoft.com/en-us/library/windows/desktop/ms682425(v=vs.85).aspx - // [2]: https://blogs.msdn.microsoft.com/oldnewthing/20031210-00/?p=41553 - - let estimated_command_line_len = - self.args.iter().map(|a| a.len()).sum::(); - estimated_command_line_len > 1024 * 6 - } -} - -impl fmt::Debug for Command { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - self.command().fmt(f) - } -} diff --git a/src/librustc_trans/back/link.rs b/src/librustc_trans/back/link.rs deleted file mode 100644 index 299075ed03a..00000000000 --- a/src/librustc_trans/back/link.rs +++ /dev/null @@ -1,1626 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use back::wasm; -use cc::windows_registry; -use super::archive::{ArchiveBuilder, ArchiveConfig}; -use super::bytecode::RLIB_BYTECODE_EXTENSION; -use super::linker::Linker; -use super::command::Command; -use super::rpath::RPathConfig; -use super::rpath; -use metadata::METADATA_FILENAME; -use rustc::session::config::{self, NoDebugInfo, OutputFilenames, OutputType, PrintRequest}; -use rustc::session::config::{RUST_CGU_EXT, Lto}; -use rustc::session::filesearch; -use rustc::session::search_paths::PathKind; -use rustc::session::Session; -use rustc::middle::cstore::{NativeLibrary, LibSource, NativeLibraryKind}; -use rustc::middle::dependency_format::Linkage; -use {CrateTranslation, CrateInfo}; -use rustc::util::common::time; -use rustc::util::fs::fix_windows_verbatim_for_gcc; -use rustc::hir::def_id::CrateNum; -use tempdir::TempDir; -use rustc_target::spec::{PanicStrategy, RelroLevel, LinkerFlavor, TargetTriple}; -use rustc_data_structures::fx::FxHashSet; -use context::get_reloc_model; -use llvm; - -use std::ascii; -use std::char; -use std::env; -use std::fmt; -use std::fs; -use std::io; -use std::path::{Path, PathBuf}; -use std::process::{Output, Stdio}; -use std::str; -use syntax::attr; - -/// The LLVM module name containing crate-metadata. This includes a `.` on -/// purpose, so it cannot clash with the name of a user-defined module. -pub const METADATA_MODULE_NAME: &'static str = "crate.metadata"; - -// same as for metadata above, but for allocator shim -pub const ALLOCATOR_MODULE_NAME: &'static str = "crate.allocator"; - -pub use rustc_trans_utils::link::{find_crate_name, filename_for_input, default_output_for_target, - invalid_output_for_target, build_link_meta, out_filename, - check_file_is_writeable}; - -// The third parameter is for env vars, used on windows to set up the -// path for MSVC to find its DLLs, and gcc to find its bundled -// toolchain -pub fn get_linker(sess: &Session) -> (PathBuf, Command) { - // If our linker looks like a batch script on Windows then to execute this - // we'll need to spawn `cmd` explicitly. This is primarily done to handle - // emscripten where the linker is `emcc.bat` and needs to be spawned as - // `cmd /c emcc.bat ...`. - // - // This worked historically but is needed manually since #42436 (regression - // was tagged as #42791) and some more info can be found on #44443 for - // emscripten itself. - let cmd = |linker: &Path| { - if let Some(linker) = linker.to_str() { - if cfg!(windows) && linker.ends_with(".bat") { - return Command::bat_script(linker) - } - } - match sess.linker_flavor() { - LinkerFlavor::Lld(f) => Command::lld(linker, f), - _ => Command::new(linker), - - } - }; - - let msvc_tool = windows_registry::find_tool(&sess.opts.target_triple.triple(), "link.exe"); - - let linker_path = sess.opts.cg.linker.as_ref().map(|s| &**s) - .or(sess.target.target.options.linker.as_ref().map(|s| s.as_ref())) - .unwrap_or(match sess.linker_flavor() { - LinkerFlavor::Msvc => { - msvc_tool.as_ref().map(|t| t.path()).unwrap_or("link.exe".as_ref()) - } - LinkerFlavor::Em if cfg!(windows) => "emcc.bat".as_ref(), - LinkerFlavor::Em => "emcc".as_ref(), - LinkerFlavor::Gcc => "cc".as_ref(), - LinkerFlavor::Ld => "ld".as_ref(), - LinkerFlavor::Lld(_) => "lld".as_ref(), - }); - - let mut cmd = cmd(linker_path); - - // The compiler's sysroot often has some bundled tools, so add it to the - // PATH for the child. - let mut new_path = sess.host_filesearch(PathKind::All) - .get_tools_search_paths(); - let mut msvc_changed_path = false; - if sess.target.target.options.is_like_msvc { - if let Some(ref tool) = msvc_tool { - cmd.args(tool.args()); - for &(ref k, ref v) in tool.env() { - if k == "PATH" { - new_path.extend(env::split_paths(v)); - msvc_changed_path = true; - } else { - cmd.env(k, v); - } - } - } - } - - if !msvc_changed_path { - if let Some(path) = env::var_os("PATH") { - new_path.extend(env::split_paths(&path)); - } - } - cmd.env("PATH", env::join_paths(new_path).unwrap()); - - (linker_path.to_path_buf(), cmd) -} - -pub fn remove(sess: &Session, path: &Path) { - match fs::remove_file(path) { - Ok(..) => {} - Err(e) => { - sess.err(&format!("failed to remove {}: {}", - path.display(), - e)); - } - } -} - -/// Perform the linkage portion of the compilation phase. This will generate all -/// of the requested outputs for this compilation session. -pub(crate) fn link_binary(sess: &Session, - trans: &CrateTranslation, - outputs: &OutputFilenames, - crate_name: &str) -> Vec { - let mut out_filenames = Vec::new(); - for &crate_type in sess.crate_types.borrow().iter() { - // Ignore executable crates if we have -Z no-trans, as they will error. - let output_metadata = sess.opts.output_types.contains_key(&OutputType::Metadata); - if (sess.opts.debugging_opts.no_trans || !sess.opts.output_types.should_trans()) && - !output_metadata && - crate_type == config::CrateTypeExecutable { - continue; - } - - if invalid_output_for_target(sess, crate_type) { - bug!("invalid output type `{:?}` for target os `{}`", - crate_type, sess.opts.target_triple); - } - let mut out_files = link_binary_output(sess, - trans, - crate_type, - outputs, - crate_name); - out_filenames.append(&mut out_files); - } - - // Remove the temporary object file and metadata if we aren't saving temps - if !sess.opts.cg.save_temps { - if sess.opts.output_types.should_trans() && - !preserve_objects_for_their_debuginfo(sess) - { - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - remove(sess, obj); - } - } - for obj in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { - remove(sess, obj); - } - if let Some(ref obj) = trans.metadata_module.object { - remove(sess, obj); - } - if let Some(ref allocator) = trans.allocator_module { - if let Some(ref obj) = allocator.object { - remove(sess, obj); - } - if let Some(ref bc) = allocator.bytecode_compressed { - remove(sess, bc); - } - } - } - - out_filenames -} - -/// Returns a boolean indicating whether we should preserve the object files on -/// the filesystem for their debug information. This is often useful with -/// split-dwarf like schemes. -fn preserve_objects_for_their_debuginfo(sess: &Session) -> bool { - // If the objects don't have debuginfo there's nothing to preserve. - if sess.opts.debuginfo == NoDebugInfo { - return false - } - - // If we're only producing artifacts that are archives, no need to preserve - // the objects as they're losslessly contained inside the archives. - let output_linked = sess.crate_types.borrow() - .iter() - .any(|x| *x != config::CrateTypeRlib && *x != config::CrateTypeStaticlib); - if !output_linked { - return false - } - - // If we're on OSX then the equivalent of split dwarf is turned on by - // default. The final executable won't actually have any debug information - // except it'll have pointers to elsewhere. Historically we've always run - // `dsymutil` to "link all the dwarf together" but this is actually sort of - // a bummer for incremental compilation! (the whole point of split dwarf is - // that you don't do this sort of dwarf link). - // - // Basically as a result this just means that if we're on OSX and we're - // *not* running dsymutil then the object files are the only source of truth - // for debug information, so we must preserve them. - if sess.target.target.options.is_like_osx { - match sess.opts.debugging_opts.run_dsymutil { - // dsymutil is not being run, preserve objects - Some(false) => return true, - - // dsymutil is being run, no need to preserve the objects - Some(true) => return false, - - // The default historical behavior was to always run dsymutil, so - // we're preserving that temporarily, but we're likely to switch the - // default soon. - None => return false, - } - } - - false -} - -fn filename_for_metadata(sess: &Session, crate_name: &str, outputs: &OutputFilenames) -> PathBuf { - let out_filename = outputs.single_output_file.clone() - .unwrap_or(outputs - .out_directory - .join(&format!("lib{}{}.rmeta", crate_name, sess.opts.cg.extra_filename))); - check_file_is_writeable(&out_filename, sess); - out_filename -} - -pub(crate) fn each_linked_rlib(sess: &Session, - info: &CrateInfo, - f: &mut FnMut(CrateNum, &Path)) -> Result<(), String> { - let crates = info.used_crates_static.iter(); - let fmts = sess.dependency_formats.borrow(); - let fmts = fmts.get(&config::CrateTypeExecutable) - .or_else(|| fmts.get(&config::CrateTypeStaticlib)) - .or_else(|| fmts.get(&config::CrateTypeCdylib)) - .or_else(|| fmts.get(&config::CrateTypeProcMacro)); - let fmts = match fmts { - Some(f) => f, - None => return Err(format!("could not find formats for rlibs")) - }; - for &(cnum, ref path) in crates { - match fmts.get(cnum.as_usize() - 1) { - Some(&Linkage::NotLinked) | - Some(&Linkage::IncludedFromDylib) => continue, - Some(_) => {} - None => return Err(format!("could not find formats for rlibs")) - } - let name = &info.crate_name[&cnum]; - let path = match *path { - LibSource::Some(ref p) => p, - LibSource::MetadataOnly => { - return Err(format!("could not find rlib for: `{}`, found rmeta (metadata) file", - name)) - } - LibSource::None => { - return Err(format!("could not find rlib for: `{}`", name)) - } - }; - f(cnum, &path); - } - Ok(()) -} - -/// Returns a boolean indicating whether the specified crate should be ignored -/// during LTO. -/// -/// Crates ignored during LTO are not lumped together in the "massive object -/// file" that we create and are linked in their normal rlib states. See -/// comments below for what crates do not participate in LTO. -/// -/// It's unusual for a crate to not participate in LTO. Typically only -/// compiler-specific and unstable crates have a reason to not participate in -/// LTO. -pub(crate) fn ignored_for_lto(sess: &Session, info: &CrateInfo, cnum: CrateNum) -> bool { - // If our target enables builtin function lowering in LLVM then the - // crates providing these functions don't participate in LTO (e.g. - // no_builtins or compiler builtins crates). - !sess.target.target.options.no_builtins && - (info.is_no_builtins.contains(&cnum) || info.compiler_builtins == Some(cnum)) -} - -fn link_binary_output(sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) -> Vec { - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - check_file_is_writeable(obj, sess); - } - - let mut out_filenames = vec![]; - - if outputs.outputs.contains_key(&OutputType::Metadata) { - let out_filename = filename_for_metadata(sess, crate_name, outputs); - // To avoid races with another rustc process scanning the output directory, - // we need to write the file somewhere else and atomically move it to its - // final destination, with a `fs::rename` call. In order for the rename to - // always succeed, the temporary file needs to be on the same filesystem, - // which is why we create it inside the output directory specifically. - let metadata_tmpdir = match TempDir::new_in(out_filename.parent().unwrap(), "rmeta") { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; - let metadata = emit_metadata(sess, trans, &metadata_tmpdir); - if let Err(e) = fs::rename(metadata, &out_filename) { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - out_filenames.push(out_filename); - } - - let tmpdir = match TempDir::new("rustc") { - Ok(tmpdir) => tmpdir, - Err(err) => sess.fatal(&format!("couldn't create a temp dir: {}", err)), - }; - - if outputs.outputs.should_trans() { - let out_filename = out_filename(sess, crate_type, outputs, crate_name); - match crate_type { - config::CrateTypeRlib => { - link_rlib(sess, - trans, - RlibFlavor::Normal, - &out_filename, - &tmpdir).build(); - } - config::CrateTypeStaticlib => { - link_staticlib(sess, trans, &out_filename, &tmpdir); - } - _ => { - link_natively(sess, crate_type, &out_filename, trans, tmpdir.path()); - } - } - out_filenames.push(out_filename); - } - - if sess.opts.cg.save_temps { - let _ = tmpdir.into_path(); - } - - out_filenames -} - -fn archive_search_paths(sess: &Session) -> Vec { - let mut search = Vec::new(); - sess.target_filesearch(PathKind::Native).for_each_lib_search_path(|path, _| { - search.push(path.to_path_buf()); - }); - return search; -} - -fn archive_config<'a>(sess: &'a Session, - output: &Path, - input: Option<&Path>) -> ArchiveConfig<'a> { - ArchiveConfig { - sess, - dst: output.to_path_buf(), - src: input.map(|p| p.to_path_buf()), - lib_search_paths: archive_search_paths(sess), - } -} - -/// We use a temp directory here to avoid races between concurrent rustc processes, -/// such as builds in the same directory using the same filename for metadata while -/// building an `.rlib` (stomping over one another), or writing an `.rmeta` into a -/// directory being searched for `extern crate` (observing an incomplete file). -/// The returned path is the temporary file containing the complete metadata. -fn emit_metadata<'a>(sess: &'a Session, trans: &CrateTranslation, tmpdir: &TempDir) - -> PathBuf { - let out_filename = tmpdir.path().join(METADATA_FILENAME); - let result = fs::write(&out_filename, &trans.metadata.raw_data); - - if let Err(e) = result { - sess.fatal(&format!("failed to write {}: {}", out_filename.display(), e)); - } - - out_filename -} - -enum RlibFlavor { - Normal, - StaticlibBase, -} - -// Create an 'rlib' -// -// An rlib in its current incarnation is essentially a renamed .a file. The -// rlib primarily contains the object file of the crate, but it also contains -// all of the object files from native libraries. This is done by unzipping -// native libraries and inserting all of the contents into this archive. -fn link_rlib<'a>(sess: &'a Session, - trans: &CrateTranslation, - flavor: RlibFlavor, - out_filename: &Path, - tmpdir: &TempDir) -> ArchiveBuilder<'a> { - info!("preparing rlib to {:?}", out_filename); - let mut ab = ArchiveBuilder::new(archive_config(sess, out_filename, None)); - - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - ab.add_file(obj); - } - - // Note that in this loop we are ignoring the value of `lib.cfg`. That is, - // we may not be configured to actually include a static library if we're - // adding it here. That's because later when we consume this rlib we'll - // decide whether we actually needed the static library or not. - // - // To do this "correctly" we'd need to keep track of which libraries added - // which object files to the archive. We don't do that here, however. The - // #[link(cfg(..))] feature is unstable, though, and only intended to get - // liblibc working. In that sense the check below just indicates that if - // there are any libraries we want to omit object files for at link time we - // just exclude all custom object files. - // - // Eventually if we want to stabilize or flesh out the #[link(cfg(..))] - // feature then we'll need to figure out how to record what objects were - // loaded from the libraries found here and then encode that into the - // metadata of the rlib we're generating somehow. - for lib in trans.crate_info.used_libraries.iter() { - match lib.kind { - NativeLibraryKind::NativeStatic => {} - NativeLibraryKind::NativeStaticNobundle | - NativeLibraryKind::NativeFramework | - NativeLibraryKind::NativeUnknown => continue, - } - ab.add_native_library(&lib.name.as_str()); - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. - ab.update_symbols(); - - // Note that it is important that we add all of our non-object "magical - // files" *after* all of the object files in the archive. The reason for - // this is as follows: - // - // * When performing LTO, this archive will be modified to remove - // objects from above. The reason for this is described below. - // - // * When the system linker looks at an archive, it will attempt to - // determine the architecture of the archive in order to see whether its - // linkable. - // - // The algorithm for this detection is: iterate over the files in the - // archive. Skip magical SYMDEF names. Interpret the first file as an - // object file. Read architecture from the object file. - // - // * As one can probably see, if "metadata" and "foo.bc" were placed - // before all of the objects, then the architecture of this archive would - // not be correctly inferred once 'foo.o' is removed. - // - // Basically, all this means is that this code should not move above the - // code above. - match flavor { - RlibFlavor::Normal => { - // Instead of putting the metadata in an object file section, rlibs - // contain the metadata in a separate file. - ab.add_file(&emit_metadata(sess, trans, tmpdir)); - - // For LTO purposes, the bytecode of this library is also inserted - // into the archive. - for bytecode in trans.modules.iter().filter_map(|m| m.bytecode_compressed.as_ref()) { - ab.add_file(bytecode); - } - - // After adding all files to the archive, we need to update the - // symbol table of the archive. This currently dies on macOS (see - // #11162), and isn't necessary there anyway - if !sess.target.target.options.is_like_osx { - ab.update_symbols(); - } - } - - RlibFlavor::StaticlibBase => { - let obj = trans.allocator_module - .as_ref() - .and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - ab.add_file(obj); - } - } - } - - ab -} - -// Create a static archive -// -// This is essentially the same thing as an rlib, but it also involves adding -// all of the upstream crates' objects into the archive. This will slurp in -// all of the native libraries of upstream dependencies as well. -// -// Additionally, there's no way for us to link dynamic libraries, so we warn -// about all dynamic library dependencies that they're not linked in. -// -// There's no need to include metadata in a static archive, so ensure to not -// link in the metadata object file (and also don't prepare the archive with a -// metadata file). -fn link_staticlib(sess: &Session, - trans: &CrateTranslation, - out_filename: &Path, - tempdir: &TempDir) { - let mut ab = link_rlib(sess, - trans, - RlibFlavor::StaticlibBase, - out_filename, - tempdir); - let mut all_native_libs = vec![]; - - let res = each_linked_rlib(sess, &trans.crate_info, &mut |cnum, path| { - let name = &trans.crate_info.crate_name[&cnum]; - let native_libs = &trans.crate_info.native_libraries[&cnum]; - - // Here when we include the rlib into our staticlib we need to make a - // decision whether to include the extra object files along the way. - // These extra object files come from statically included native - // libraries, but they may be cfg'd away with #[link(cfg(..))]. - // - // This unstable feature, though, only needs liblibc to work. The only - // use case there is where musl is statically included in liblibc.rlib, - // so if we don't want the included version we just need to skip it. As - // a result the logic here is that if *any* linked library is cfg'd away - // we just skip all object files. - // - // Clearly this is not sufficient for a general purpose feature, and - // we'd want to read from the library's metadata to determine which - // object files come from where and selectively skip them. - let skip_object_files = native_libs.iter().any(|lib| { - lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) - }); - ab.add_rlib(path, - &name.as_str(), - is_full_lto_enabled(sess) && - !ignored_for_lto(sess, &trans.crate_info, cnum), - skip_object_files).unwrap(); - - all_native_libs.extend(trans.crate_info.native_libraries[&cnum].iter().cloned()); - }); - if let Err(e) = res { - sess.fatal(&e); - } - - ab.update_symbols(); - ab.build(); - - if !all_native_libs.is_empty() { - if sess.opts.prints.contains(&PrintRequest::NativeStaticLibs) { - print_native_static_libs(sess, &all_native_libs); - } - } -} - -fn print_native_static_libs(sess: &Session, all_native_libs: &[NativeLibrary]) { - let lib_args: Vec<_> = all_native_libs.iter() - .filter(|l| relevant_lib(sess, l)) - .filter_map(|lib| match lib.kind { - NativeLibraryKind::NativeStaticNobundle | - NativeLibraryKind::NativeUnknown => { - if sess.target.target.options.is_like_msvc { - Some(format!("{}.lib", lib.name)) - } else { - Some(format!("-l{}", lib.name)) - } - }, - NativeLibraryKind::NativeFramework => { - // ld-only syntax, since there are no frameworks in MSVC - Some(format!("-framework {}", lib.name)) - }, - // These are included, no need to print them - NativeLibraryKind::NativeStatic => None, - }) - .collect(); - if !lib_args.is_empty() { - sess.note_without_error("Link against the following native artifacts when linking \ - against this static library. The order and any duplication \ - can be significant on some platforms."); - // Prefix for greppability - sess.note_without_error(&format!("native-static-libs: {}", &lib_args.join(" "))); - } -} - -// Create a dynamic library or executable -// -// This will invoke the system linker/cc to create the resulting file. This -// links to all upstream files as well. -fn link_natively(sess: &Session, - crate_type: config::CrateType, - out_filename: &Path, - trans: &CrateTranslation, - tmpdir: &Path) { - info!("preparing {:?} to {:?}", crate_type, out_filename); - let flavor = sess.linker_flavor(); - - // The invocations of cc share some flags across platforms - let (pname, mut cmd) = get_linker(sess); - - let root = sess.target_filesearch(PathKind::Native).get_lib_path(); - if let Some(args) = sess.target.target.options.pre_link_args.get(&flavor) { - cmd.args(args); - } - if let Some(args) = sess.target.target.options.pre_link_args_crt.get(&flavor) { - if sess.crt_static() { - cmd.args(args); - } - } - if let Some(ref args) = sess.opts.debugging_opts.pre_link_args { - cmd.args(args); - } - cmd.args(&sess.opts.debugging_opts.pre_link_arg); - - let pre_link_objects = if crate_type == config::CrateTypeExecutable { - &sess.target.target.options.pre_link_objects_exe - } else { - &sess.target.target.options.pre_link_objects_dll - }; - for obj in pre_link_objects { - cmd.arg(root.join(obj)); - } - - if crate_type == config::CrateTypeExecutable && sess.crt_static() { - for obj in &sess.target.target.options.pre_link_objects_exe_crt { - cmd.arg(root.join(obj)); - } - - for obj in &sess.target.target.options.pre_link_objects_exe_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - } - - if sess.target.target.options.is_like_emscripten { - cmd.arg("-s"); - cmd.arg(if sess.panic_strategy() == PanicStrategy::Abort { - "DISABLE_EXCEPTION_CATCHING=1" - } else { - "DISABLE_EXCEPTION_CATCHING=0" - }); - } - - { - let mut linker = trans.linker_info.to_linker(cmd, &sess); - link_args(&mut *linker, sess, crate_type, tmpdir, - out_filename, trans); - cmd = linker.finalize(); - } - if let Some(args) = sess.target.target.options.late_link_args.get(&flavor) { - cmd.args(args); - } - for obj in &sess.target.target.options.post_link_objects { - cmd.arg(root.join(obj)); - } - if sess.crt_static() { - for obj in &sess.target.target.options.post_link_objects_crt_sys { - if flavor == LinkerFlavor::Gcc { - cmd.arg(format!("-l:{}", obj)); - } - } - for obj in &sess.target.target.options.post_link_objects_crt { - cmd.arg(root.join(obj)); - } - } - if let Some(args) = sess.target.target.options.post_link_args.get(&flavor) { - cmd.args(args); - } - for &(ref k, ref v) in &sess.target.target.options.link_env { - cmd.env(k, v); - } - - if sess.opts.debugging_opts.print_link_args { - println!("{:?}", &cmd); - } - - // May have not found libraries in the right formats. - sess.abort_if_errors(); - - // Invoke the system linker - // - // Note that there's a terribly awful hack that really shouldn't be present - // in any compiler. Here an environment variable is supported to - // automatically retry the linker invocation if the linker looks like it - // segfaulted. - // - // Gee that seems odd, normally segfaults are things we want to know about! - // Unfortunately though in rust-lang/rust#38878 we're experiencing the - // linker segfaulting on Travis quite a bit which is causing quite a bit of - // pain to land PRs when they spuriously fail due to a segfault. - // - // The issue #38878 has some more debugging information on it as well, but - // this unfortunately looks like it's just a race condition in macOS's linker - // with some thread pool working in the background. It seems that no one - // currently knows a fix for this so in the meantime we're left with this... - info!("{:?}", &cmd); - let retry_on_segfault = env::var("RUSTC_RETRY_LINKER_ON_SEGFAULT").is_ok(); - let mut prog; - let mut i = 0; - loop { - i += 1; - prog = time(sess, "running linker", || { - exec_linker(sess, &mut cmd, out_filename, tmpdir) - }); - let output = match prog { - Ok(ref output) => output, - Err(_) => break, - }; - if output.status.success() { - break - } - let mut out = output.stderr.clone(); - out.extend(&output.stdout); - let out = String::from_utf8_lossy(&out); - - // Check to see if the link failed with "unrecognized command line option: - // '-no-pie'" for gcc or "unknown argument: '-no-pie'" for clang. If so, - // reperform the link step without the -no-pie option. This is safe because - // if the linker doesn't support -no-pie then it should not default to - // linking executables as pie. Different versions of gcc seem to use - // different quotes in the error message so don't check for them. - if sess.target.target.options.linker_is_gnu && - sess.linker_flavor() != LinkerFlavor::Ld && - (out.contains("unrecognized command line option") || - out.contains("unknown argument")) && - out.contains("-no-pie") && - cmd.get_args().iter().any(|e| e.to_string_lossy() == "-no-pie") { - info!("linker output: {:?}", out); - warn!("Linker does not support -no-pie command line option. Retrying without."); - for arg in cmd.take_args() { - if arg.to_string_lossy() != "-no-pie" { - cmd.arg(arg); - } - } - info!("{:?}", &cmd); - continue; - } - if !retry_on_segfault || i > 3 { - break - } - let msg_segv = "clang: error: unable to execute command: Segmentation fault: 11"; - let msg_bus = "clang: error: unable to execute command: Bus error: 10"; - if !(out.contains(msg_segv) || out.contains(msg_bus)) { - break - } - - warn!( - "looks like the linker segfaulted when we tried to call it, \ - automatically retrying again. cmd = {:?}, out = {}.", - cmd, - out, - ); - } - - match prog { - Ok(prog) => { - fn escape_string(s: &[u8]) -> String { - str::from_utf8(s).map(|s| s.to_owned()) - .unwrap_or_else(|_| { - let mut x = "Non-UTF-8 output: ".to_string(); - x.extend(s.iter() - .flat_map(|&b| ascii::escape_default(b)) - .map(|b| char::from_u32(b as u32).unwrap())); - x - }) - } - if !prog.status.success() { - let mut output = prog.stderr.clone(); - output.extend_from_slice(&prog.stdout); - sess.struct_err(&format!("linking with `{}` failed: {}", - pname.display(), - prog.status)) - .note(&format!("{:?}", &cmd)) - .note(&escape_string(&output)) - .emit(); - sess.abort_if_errors(); - } - info!("linker stderr:\n{}", escape_string(&prog.stderr)); - info!("linker stdout:\n{}", escape_string(&prog.stdout)); - }, - Err(e) => { - let linker_not_found = e.kind() == io::ErrorKind::NotFound; - - let mut linker_error = { - if linker_not_found { - sess.struct_err(&format!("linker `{}` not found", pname.display())) - } else { - sess.struct_err(&format!("could not exec the linker `{}`", pname.display())) - } - }; - - linker_error.note(&format!("{}", e)); - - if !linker_not_found { - linker_error.note(&format!("{:?}", &cmd)); - } - - linker_error.emit(); - - if sess.target.target.options.is_like_msvc && linker_not_found { - sess.note_without_error("the msvc targets depend on the msvc linker \ - but `link.exe` was not found"); - sess.note_without_error("please ensure that VS 2013 or VS 2015 was installed \ - with the Visual C++ option"); - } - sess.abort_if_errors(); - } - } - - - // On macOS, debuggers need this utility to get run to do some munging of - // the symbols. Note, though, that if the object files are being preserved - // for their debug information there's no need for us to run dsymutil. - if sess.target.target.options.is_like_osx && - sess.opts.debuginfo != NoDebugInfo && - !preserve_objects_for_their_debuginfo(sess) - { - match Command::new("dsymutil").arg(out_filename).output() { - Ok(..) => {} - Err(e) => sess.fatal(&format!("failed to run dsymutil: {}", e)), - } - } - - if sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown") { - wasm::rewrite_imports(&out_filename, &trans.crate_info.wasm_imports); - wasm::add_custom_sections(&out_filename, - &trans.crate_info.wasm_custom_sections); - } -} - -fn exec_linker(sess: &Session, cmd: &mut Command, out_filename: &Path, tmpdir: &Path) - -> io::Result -{ - // When attempting to spawn the linker we run a risk of blowing out the - // size limits for spawning a new process with respect to the arguments - // we pass on the command line. - // - // Here we attempt to handle errors from the OS saying "your list of - // arguments is too big" by reinvoking the linker again with an `@`-file - // that contains all the arguments. The theory is that this is then - // accepted on all linkers and the linker will read all its options out of - // there instead of looking at the command line. - if !cmd.very_likely_to_exceed_some_spawn_limit() { - match cmd.command().stdout(Stdio::piped()).stderr(Stdio::piped()).spawn() { - Ok(child) => { - let output = child.wait_with_output(); - flush_linked_file(&output, out_filename)?; - return output; - } - Err(ref e) if command_line_too_big(e) => { - info!("command line to linker was too big: {}", e); - } - Err(e) => return Err(e) - } - } - - info!("falling back to passing arguments to linker via an @-file"); - let mut cmd2 = cmd.clone(); - let mut args = String::new(); - for arg in cmd2.take_args() { - args.push_str(&Escape { - arg: arg.to_str().unwrap(), - is_like_msvc: sess.target.target.options.is_like_msvc, - }.to_string()); - args.push_str("\n"); - } - let file = tmpdir.join("linker-arguments"); - let bytes = if sess.target.target.options.is_like_msvc { - let mut out = vec![]; - // start the stream with a UTF-16 BOM - for c in vec![0xFEFF].into_iter().chain(args.encode_utf16()) { - // encode in little endian - out.push(c as u8); - out.push((c >> 8) as u8); - } - out - } else { - args.into_bytes() - }; - fs::write(&file, &bytes)?; - cmd2.arg(format!("@{}", file.display())); - info!("invoking linker {:?}", cmd2); - let output = cmd2.output(); - flush_linked_file(&output, out_filename)?; - return output; - - #[cfg(unix)] - fn flush_linked_file(_: &io::Result, _: &Path) -> io::Result<()> { - Ok(()) - } - - #[cfg(windows)] - fn flush_linked_file(command_output: &io::Result, out_filename: &Path) - -> io::Result<()> - { - // On Windows, under high I/O load, output buffers are sometimes not flushed, - // even long after process exit, causing nasty, non-reproducible output bugs. - // - // File::sync_all() calls FlushFileBuffers() down the line, which solves the problem. - // - // А full writeup of the original Chrome bug can be found at - // randomascii.wordpress.com/2018/02/25/compiler-bug-linker-bug-windows-kernel-bug/amp - - if let &Ok(ref out) = command_output { - if out.status.success() { - if let Ok(of) = fs::OpenOptions::new().write(true).open(out_filename) { - of.sync_all()?; - } - } - } - - Ok(()) - } - - #[cfg(unix)] - fn command_line_too_big(err: &io::Error) -> bool { - err.raw_os_error() == Some(::libc::E2BIG) - } - - #[cfg(windows)] - fn command_line_too_big(err: &io::Error) -> bool { - const ERROR_FILENAME_EXCED_RANGE: i32 = 206; - err.raw_os_error() == Some(ERROR_FILENAME_EXCED_RANGE) - } - - struct Escape<'a> { - arg: &'a str, - is_like_msvc: bool, - } - - impl<'a> fmt::Display for Escape<'a> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - if self.is_like_msvc { - // This is "documented" at - // https://msdn.microsoft.com/en-us/library/4xdcbak7.aspx - // - // Unfortunately there's not a great specification of the - // syntax I could find online (at least) but some local - // testing showed that this seemed sufficient-ish to catch - // at least a few edge cases. - write!(f, "\"")?; - for c in self.arg.chars() { - match c { - '"' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, - } - } - write!(f, "\"")?; - } else { - // This is documented at https://linux.die.net/man/1/ld, namely: - // - // > Options in file are separated by whitespace. A whitespace - // > character may be included in an option by surrounding the - // > entire option in either single or double quotes. Any - // > character (including a backslash) may be included by - // > prefixing the character to be included with a backslash. - // - // We put an argument on each line, so all we need to do is - // ensure the line is interpreted as one whole argument. - for c in self.arg.chars() { - match c { - '\\' | - ' ' => write!(f, "\\{}", c)?, - c => write!(f, "{}", c)?, - } - } - } - Ok(()) - } - } -} - -fn link_args(cmd: &mut Linker, - sess: &Session, - crate_type: config::CrateType, - tmpdir: &Path, - out_filename: &Path, - trans: &CrateTranslation) { - - // Linker plugins should be specified early in the list of arguments - cmd.cross_lang_lto(); - - // The default library location, we need this to find the runtime. - // The location of crates will be determined as needed. - let lib_path = sess.target_filesearch(PathKind::All).get_lib_path(); - - // target descriptor - let t = &sess.target.target; - - cmd.include_path(&fix_windows_verbatim_for_gcc(&lib_path)); - for obj in trans.modules.iter().filter_map(|m| m.object.as_ref()) { - cmd.add_object(obj); - } - cmd.output_filename(out_filename); - - if crate_type == config::CrateTypeExecutable && - sess.target.target.options.is_like_windows { - if let Some(ref s) = trans.windows_subsystem { - cmd.subsystem(s); - } - } - - // If we're building a dynamic library then some platforms need to make sure - // that all symbols are exported correctly from the dynamic library. - if crate_type != config::CrateTypeExecutable || - sess.target.target.options.is_like_emscripten { - cmd.export_symbols(tmpdir, crate_type); - } - - // When linking a dynamic library, we put the metadata into a section of the - // executable. This metadata is in a separate object file from the main - // object file, so we link that in here. - if crate_type == config::CrateTypeDylib || - crate_type == config::CrateTypeProcMacro { - if let Some(obj) = trans.metadata_module.object.as_ref() { - cmd.add_object(obj); - } - } - - let obj = trans.allocator_module - .as_ref() - .and_then(|m| m.object.as_ref()); - if let Some(obj) = obj { - cmd.add_object(obj); - } - - // Try to strip as much out of the generated object by removing unused - // sections if possible. See more comments in linker.rs - if !sess.opts.cg.link_dead_code { - let keep_metadata = crate_type == config::CrateTypeDylib; - cmd.gc_sections(keep_metadata); - } - - let used_link_args = &trans.crate_info.link_args; - - if crate_type == config::CrateTypeExecutable { - let mut position_independent_executable = false; - - if t.options.position_independent_executables { - let empty_vec = Vec::new(); - let args = sess.opts.cg.link_args.as_ref().unwrap_or(&empty_vec); - let more_args = &sess.opts.cg.link_arg; - let mut args = args.iter().chain(more_args.iter()).chain(used_link_args.iter()); - - if get_reloc_model(sess) == llvm::RelocMode::PIC - && !sess.crt_static() && !args.any(|x| *x == "-static") { - position_independent_executable = true; - } - } - - if position_independent_executable { - cmd.position_independent_executable(); - } else { - // recent versions of gcc can be configured to generate position - // independent executables by default. We have to pass -no-pie to - // explicitly turn that off. Not applicable to ld. - if sess.target.target.options.linker_is_gnu - && sess.linker_flavor() != LinkerFlavor::Ld { - cmd.no_position_independent_executable(); - } - } - } - - let relro_level = match sess.opts.debugging_opts.relro_level { - Some(level) => level, - None => t.options.relro_level, - }; - match relro_level { - RelroLevel::Full => { - cmd.full_relro(); - }, - RelroLevel::Partial => { - cmd.partial_relro(); - }, - RelroLevel::Off => { - cmd.no_relro(); - }, - RelroLevel::None => { - }, - } - - // Pass optimization flags down to the linker. - cmd.optimize(); - - // Pass debuginfo flags down to the linker. - cmd.debuginfo(); - - // We want to prevent the compiler from accidentally leaking in any system - // libraries, so we explicitly ask gcc to not link to any libraries by - // default. Note that this does not happen for windows because windows pulls - // in some large number of libraries and I couldn't quite figure out which - // subset we wanted. - if t.options.no_default_libraries { - cmd.no_default_libraries(); - } - - // Take careful note of the ordering of the arguments we pass to the linker - // here. Linkers will assume that things on the left depend on things to the - // right. Things on the right cannot depend on things on the left. This is - // all formally implemented in terms of resolving symbols (libs on the right - // resolve unknown symbols of libs on the left, but not vice versa). - // - // For this reason, we have organized the arguments we pass to the linker as - // such: - // - // 1. The local object that LLVM just generated - // 2. Local native libraries - // 3. Upstream rust libraries - // 4. Upstream native libraries - // - // The rationale behind this ordering is that those items lower down in the - // list can't depend on items higher up in the list. For example nothing can - // depend on what we just generated (e.g. that'd be a circular dependency). - // Upstream rust libraries are not allowed to depend on our local native - // libraries as that would violate the structure of the DAG, in that - // scenario they are required to link to them as well in a shared fashion. - // - // Note that upstream rust libraries may contain native dependencies as - // well, but they also can't depend on what we just started to add to the - // link line. And finally upstream native libraries can't depend on anything - // in this DAG so far because they're only dylibs and dylibs can only depend - // on other dylibs (e.g. other native deps). - add_local_native_libraries(cmd, sess, trans); - add_upstream_rust_crates(cmd, sess, trans, crate_type, tmpdir); - add_upstream_native_libraries(cmd, sess, trans, crate_type); - - // Tell the linker what we're doing. - if crate_type != config::CrateTypeExecutable { - cmd.build_dylib(out_filename); - } - if crate_type == config::CrateTypeExecutable && sess.crt_static() { - cmd.build_static_executable(); - } - - if sess.opts.debugging_opts.pgo_gen.is_some() { - cmd.pgo_gen(); - } - - // FIXME (#2397): At some point we want to rpath our guesses as to - // where extern libraries might live, based on the - // addl_lib_search_paths - if sess.opts.cg.rpath { - let sysroot = sess.sysroot(); - let target_triple = sess.opts.target_triple.triple(); - let mut get_install_prefix_lib_path = || { - let install_prefix = option_env!("CFG_PREFIX").expect("CFG_PREFIX"); - let tlib = filesearch::relative_target_lib_path(sysroot, target_triple); - let mut path = PathBuf::from(install_prefix); - path.push(&tlib); - - path - }; - let mut rpath_config = RPathConfig { - used_crates: &trans.crate_info.used_crates_dynamic, - out_filename: out_filename.to_path_buf(), - has_rpath: sess.target.target.options.has_rpath, - is_like_osx: sess.target.target.options.is_like_osx, - linker_is_gnu: sess.target.target.options.linker_is_gnu, - get_install_prefix_lib_path: &mut get_install_prefix_lib_path, - }; - cmd.args(&rpath::get_rpath_flags(&mut rpath_config)); - } - - // Finally add all the linker arguments provided on the command line along - // with any #[link_args] attributes found inside the crate - if let Some(ref args) = sess.opts.cg.link_args { - cmd.args(args); - } - cmd.args(&sess.opts.cg.link_arg); - cmd.args(&used_link_args); -} - -// # Native library linking -// -// User-supplied library search paths (-L on the command line). These are -// the same paths used to find Rust crates, so some of them may have been -// added already by the previous crate linking code. This only allows them -// to be found at compile time so it is still entirely up to outside -// forces to make sure that library can be found at runtime. -// -// Also note that the native libraries linked here are only the ones located -// in the current crate. Upstream crates with native library dependencies -// may have their native library pulled in above. -fn add_local_native_libraries(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation) { - sess.target_filesearch(PathKind::All).for_each_lib_search_path(|path, k| { - match k { - PathKind::Framework => { cmd.framework_path(path); } - _ => { cmd.include_path(&fix_windows_verbatim_for_gcc(path)); } - } - }); - - let relevant_libs = trans.crate_info.used_libraries.iter().filter(|l| { - relevant_lib(sess, l) - }); - - let search_path = archive_search_paths(sess); - for lib in relevant_libs { - match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), - NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), - NativeLibraryKind::NativeStaticNobundle => cmd.link_staticlib(&lib.name.as_str()), - NativeLibraryKind::NativeStatic => cmd.link_whole_staticlib(&lib.name.as_str(), - &search_path) - } - } -} - -// # Rust Crate linking -// -// Rust crates are not considered at all when creating an rlib output. All -// dependencies will be linked when producing the final output (instead of -// the intermediate rlib version) -fn add_upstream_rust_crates(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType, - tmpdir: &Path) { - // All of the heavy lifting has previously been accomplished by the - // dependency_format module of the compiler. This is just crawling the - // output of that module, adding crates as necessary. - // - // Linking to a rlib involves just passing it to the linker (the linker - // will slurp up the object files inside), and linking to a dynamic library - // involves just passing the right -l flag. - - let formats = sess.dependency_formats.borrow(); - let data = formats.get(&crate_type).unwrap(); - - // Invoke get_used_crates to ensure that we get a topological sorting of - // crates. - let deps = &trans.crate_info.used_crates_dynamic; - - // There's a few internal crates in the standard library (aka libcore and - // libstd) which actually have a circular dependence upon one another. This - // currently arises through "weak lang items" where libcore requires things - // like `rust_begin_unwind` but libstd ends up defining it. To get this - // circular dependence to work correctly in all situations we'll need to be - // sure to correctly apply the `--start-group` and `--end-group` options to - // GNU linkers, otherwise if we don't use any other symbol from the standard - // library it'll get discarded and the whole application won't link. - // - // In this loop we're calculating the `group_end`, after which crate to - // pass `--end-group` and `group_start`, before which crate to pass - // `--start-group`. We currently do this by passing `--end-group` after - // the first crate (when iterating backwards) that requires a lang item - // defined somewhere else. Once that's set then when we've defined all the - // necessary lang items we'll pass `--start-group`. - // - // Note that this isn't amazing logic for now but it should do the trick - // for the current implementation of the standard library. - let mut group_end = None; - let mut group_start = None; - let mut end_with = FxHashSet(); - let info = &trans.crate_info; - for &(cnum, _) in deps.iter().rev() { - if let Some(missing) = info.missing_lang_items.get(&cnum) { - end_with.extend(missing.iter().cloned()); - if end_with.len() > 0 && group_end.is_none() { - group_end = Some(cnum); - } - } - end_with.retain(|item| info.lang_item_to_crate.get(item) != Some(&cnum)); - if end_with.len() == 0 && group_end.is_some() { - group_start = Some(cnum); - break - } - } - - // If we didn't end up filling in all lang items from upstream crates then - // we'll be filling it in with our crate. This probably means we're the - // standard library itself, so skip this for now. - if group_end.is_some() && group_start.is_none() { - group_end = None; - } - - let mut compiler_builtins = None; - - for &(cnum, _) in deps.iter() { - if group_start == Some(cnum) { - cmd.group_start(); - } - - // We may not pass all crates through to the linker. Some crates may - // appear statically in an existing dylib, meaning we'll pick up all the - // symbols from the dylib. - let src = &trans.crate_info.used_crate_source[&cnum]; - match data[cnum.as_usize() - 1] { - _ if trans.crate_info.profiler_runtime == Some(cnum) => { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - _ if trans.crate_info.sanitizer_runtime == Some(cnum) => { - link_sanitizer_runtime(cmd, sess, trans, tmpdir, cnum); - } - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - _ if trans.crate_info.compiler_builtins == Some(cnum) => { - assert!(compiler_builtins.is_none()); - compiler_builtins = Some(cnum); - } - Linkage::NotLinked | - Linkage::IncludedFromDylib => {} - Linkage::Static => { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - Linkage::Dynamic => { - add_dynamic_crate(cmd, sess, &src.dylib.as_ref().unwrap().0) - } - } - - if group_end == Some(cnum) { - cmd.group_end(); - } - } - - // compiler-builtins are always placed last to ensure that they're - // linked correctly. - // We must always link the `compiler_builtins` crate statically. Even if it - // was already "included" in a dylib (e.g. `libstd` when `-C prefer-dynamic` - // is used) - if let Some(cnum) = compiler_builtins { - add_static_crate(cmd, sess, trans, tmpdir, crate_type, cnum); - } - - // Converts a library file-stem into a cc -l argument - fn unlib<'a>(config: &config::Config, stem: &'a str) -> &'a str { - if stem.starts_with("lib") && !config.target.options.is_like_windows { - &stem[3..] - } else { - stem - } - } - - // We must link the sanitizer runtime using -Wl,--whole-archive but since - // it's packed in a .rlib, it contains stuff that are not objects that will - // make the linker error. So we must remove those bits from the .rlib before - // linking it. - fn link_sanitizer_runtime(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - tmpdir: &Path, - cnum: CrateNum) { - let src = &trans.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - if sess.target.target.options.is_like_osx { - // On Apple platforms, the sanitizer is always built as a dylib, and - // LLVM will link to `@rpath/*.dylib`, so we need to specify an - // rpath to the library as well (the rpath should be absolute, see - // PR #41352 for details). - // - // FIXME: Remove this logic into librustc_*san once Cargo supports it - let rpath = cratepath.parent().unwrap(); - let rpath = rpath.to_str().expect("non-utf8 component in path"); - cmd.args(&["-Wl,-rpath".into(), "-Xlinker".into(), rpath.into()]); - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let cfg = archive_config(sess, &dst, Some(cratepath)); - let mut archive = ArchiveBuilder::new(cfg); - archive.update_symbols(); - - for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { - archive.remove_file(&f); - continue - } - } - - archive.build(); - - cmd.link_whole_rlib(&dst); - } - - // Adds the static "rlib" versions of all crates to the command line. - // There's a bit of magic which happens here specifically related to LTO and - // dynamic libraries. Specifically: - // - // * For LTO, we remove upstream object files. - // * For dylibs we remove metadata and bytecode from upstream rlibs - // - // When performing LTO, almost(*) all of the bytecode from the upstream - // libraries has already been included in our object file output. As a - // result we need to remove the object files in the upstream libraries so - // the linker doesn't try to include them twice (or whine about duplicate - // symbols). We must continue to include the rest of the rlib, however, as - // it may contain static native libraries which must be linked in. - // - // (*) Crates marked with `#![no_builtins]` don't participate in LTO and - // their bytecode wasn't included. The object files in those libraries must - // still be passed to the linker. - // - // When making a dynamic library, linkers by default don't include any - // object files in an archive if they're not necessary to resolve the link. - // We basically want to convert the archive (rlib) to a dylib, though, so we - // *do* want everything included in the output, regardless of whether the - // linker thinks it's needed or not. As a result we must use the - // --whole-archive option (or the platform equivalent). When using this - // option the linker will fail if there are non-objects in the archive (such - // as our own metadata and/or bytecode). All in all, for rlibs to be - // entirely included in dylibs, we need to remove all non-object files. - // - // Note, however, that if we're not doing LTO or we're not producing a dylib - // (aka we're making an executable), we can just pass the rlib blindly to - // the linker (fast) because it's fine if it's not actually included as - // we're at the end of the dependency chain. - fn add_static_crate(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - tmpdir: &Path, - crate_type: config::CrateType, - cnum: CrateNum) { - let src = &trans.crate_info.used_crate_source[&cnum]; - let cratepath = &src.rlib.as_ref().unwrap().0; - - // See the comment above in `link_staticlib` and `link_rlib` for why if - // there's a static library that's not relevant we skip all object - // files. - let native_libs = &trans.crate_info.native_libraries[&cnum]; - let skip_native = native_libs.iter().any(|lib| { - lib.kind == NativeLibraryKind::NativeStatic && !relevant_lib(sess, lib) - }); - - if (!is_full_lto_enabled(sess) || - ignored_for_lto(sess, &trans.crate_info, cnum)) && - crate_type != config::CrateTypeDylib && - !skip_native { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(cratepath)); - return - } - - let dst = tmpdir.join(cratepath.file_name().unwrap()); - let name = cratepath.file_name().unwrap().to_str().unwrap(); - let name = &name[3..name.len() - 5]; // chop off lib/.rlib - - time(sess, &format!("altering {}.rlib", name), || { - let cfg = archive_config(sess, &dst, Some(cratepath)); - let mut archive = ArchiveBuilder::new(cfg); - archive.update_symbols(); - - let mut any_objects = false; - for f in archive.src_files() { - if f.ends_with(RLIB_BYTECODE_EXTENSION) || f == METADATA_FILENAME { - archive.remove_file(&f); - continue - } - - let canonical = f.replace("-", "_"); - let canonical_name = name.replace("-", "_"); - - // Look for `.rcgu.o` at the end of the filename to conclude - // that this is a Rust-related object file. - fn looks_like_rust(s: &str) -> bool { - let path = Path::new(s); - let ext = path.extension().and_then(|s| s.to_str()); - if ext != Some(OutputType::Object.extension()) { - return false - } - let ext2 = path.file_stem() - .and_then(|s| Path::new(s).extension()) - .and_then(|s| s.to_str()); - ext2 == Some(RUST_CGU_EXT) - } - - let is_rust_object = - canonical.starts_with(&canonical_name) && - looks_like_rust(&f); - - // If we've been requested to skip all native object files - // (those not generated by the rust compiler) then we can skip - // this file. See above for why we may want to do this. - let skip_because_cfg_say_so = skip_native && !is_rust_object; - - // If we're performing LTO and this is a rust-generated object - // file, then we don't need the object file as it's part of the - // LTO module. Note that `#![no_builtins]` is excluded from LTO, - // though, so we let that object file slide. - let skip_because_lto = is_full_lto_enabled(sess) && - is_rust_object && - (sess.target.target.options.no_builtins || - !trans.crate_info.is_no_builtins.contains(&cnum)); - - if skip_because_cfg_say_so || skip_because_lto { - archive.remove_file(&f); - } else { - any_objects = true; - } - } - - if !any_objects { - return - } - archive.build(); - - // If we're creating a dylib, then we need to include the - // whole of each object in our archive into that artifact. This is - // because a `dylib` can be reused as an intermediate artifact. - // - // Note, though, that we don't want to include the whole of a - // compiler-builtins crate (e.g. compiler-rt) because it'll get - // repeatedly linked anyway. - if crate_type == config::CrateTypeDylib && - trans.crate_info.compiler_builtins != Some(cnum) { - cmd.link_whole_rlib(&fix_windows_verbatim_for_gcc(&dst)); - } else { - cmd.link_rlib(&fix_windows_verbatim_for_gcc(&dst)); - } - }); - } - - // Same thing as above, but for dynamic crates instead of static crates. - fn add_dynamic_crate(cmd: &mut Linker, sess: &Session, cratepath: &Path) { - // If we're performing LTO, then it should have been previously required - // that all upstream rust dependencies were available in an rlib format. - assert!(!is_full_lto_enabled(sess)); - - // Just need to tell the linker about where the library lives and - // what its name is - let parent = cratepath.parent(); - if let Some(dir) = parent { - cmd.include_path(&fix_windows_verbatim_for_gcc(dir)); - } - let filestem = cratepath.file_stem().unwrap().to_str().unwrap(); - cmd.link_rust_dylib(&unlib(&sess.target, filestem), - parent.unwrap_or(Path::new(""))); - } -} - -// Link in all of our upstream crates' native dependencies. Remember that -// all of these upstream native dependencies are all non-static -// dependencies. We've got two cases then: -// -// 1. The upstream crate is an rlib. In this case we *must* link in the -// native dependency because the rlib is just an archive. -// -// 2. The upstream crate is a dylib. In order to use the dylib, we have to -// have the dependency present on the system somewhere. Thus, we don't -// gain a whole lot from not linking in the dynamic dependency to this -// crate as well. -// -// The use case for this is a little subtle. In theory the native -// dependencies of a crate are purely an implementation detail of the crate -// itself, but the problem arises with generic and inlined functions. If a -// generic function calls a native function, then the generic function must -// be instantiated in the target crate, meaning that the native symbol must -// also be resolved in the target crate. -fn add_upstream_native_libraries(cmd: &mut Linker, - sess: &Session, - trans: &CrateTranslation, - crate_type: config::CrateType) { - // Be sure to use a topological sorting of crates because there may be - // interdependencies between native libraries. When passing -nodefaultlibs, - // for example, almost all native libraries depend on libc, so we have to - // make sure that's all the way at the right (liblibc is near the base of - // the dependency chain). - // - // This passes RequireStatic, but the actual requirement doesn't matter, - // we're just getting an ordering of crate numbers, we're not worried about - // the paths. - let formats = sess.dependency_formats.borrow(); - let data = formats.get(&crate_type).unwrap(); - - let crates = &trans.crate_info.used_crates_static; - for &(cnum, _) in crates { - for lib in trans.crate_info.native_libraries[&cnum].iter() { - if !relevant_lib(sess, &lib) { - continue - } - match lib.kind { - NativeLibraryKind::NativeUnknown => cmd.link_dylib(&lib.name.as_str()), - NativeLibraryKind::NativeFramework => cmd.link_framework(&lib.name.as_str()), - NativeLibraryKind::NativeStaticNobundle => { - // Link "static-nobundle" native libs only if the crate they originate from - // is being linked statically to the current crate. If it's linked dynamically - // or is an rlib already included via some other dylib crate, the symbols from - // native libs will have already been included in that dylib. - if data[cnum.as_usize() - 1] == Linkage::Static { - cmd.link_staticlib(&lib.name.as_str()) - } - }, - // ignore statically included native libraries here as we've - // already included them when we included the rust library - // previously - NativeLibraryKind::NativeStatic => {} - } - } - } -} - -fn relevant_lib(sess: &Session, lib: &NativeLibrary) -> bool { - match lib.cfg { - Some(ref cfg) => attr::cfg_matches(cfg, &sess.parse_sess, None), - None => true, - } -} - -fn is_full_lto_enabled(sess: &Session) -> bool { - match sess.lto() { - Lto::Yes | - Lto::Thin | - Lto::Fat => true, - Lto::No | - Lto::ThinLocal => false, - } -} diff --git a/src/librustc_trans/back/linker.rs b/src/librustc_trans/back/linker.rs deleted file mode 100644 index 2a84ffe79b2..00000000000 --- a/src/librustc_trans/back/linker.rs +++ /dev/null @@ -1,1037 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashMap; -use std::ffi::{OsStr, OsString}; -use std::fs::{self, File}; -use std::io::prelude::*; -use std::io::{self, BufWriter}; -use std::path::{Path, PathBuf}; - -use back::archive; -use back::command::Command; -use back::symbol_export; -use rustc::hir::def_id::{LOCAL_CRATE, CrateNum}; -use rustc::middle::dependency_format::Linkage; -use rustc::session::Session; -use rustc::session::config::{self, CrateType, OptLevel, DebugInfoLevel, - CrossLangLto}; -use rustc::ty::TyCtxt; -use rustc_target::spec::{LinkerFlavor, LldFlavor}; -use serialize::{json, Encoder}; - -/// For all the linkers we support, and information they might -/// need out of the shared crate context before we get rid of it. -pub struct LinkerInfo { - exports: HashMap>, -} - -impl LinkerInfo { - pub fn new(tcx: TyCtxt) -> LinkerInfo { - LinkerInfo { - exports: tcx.sess.crate_types.borrow().iter().map(|&c| { - (c, exported_symbols(tcx, c)) - }).collect(), - } - } - - pub fn to_linker<'a>(&'a self, - cmd: Command, - sess: &'a Session) -> Box { - match sess.linker_flavor() { - LinkerFlavor::Lld(LldFlavor::Link) | - LinkerFlavor::Msvc => { - Box::new(MsvcLinker { - cmd, - sess, - info: self - }) as Box - } - LinkerFlavor::Em => { - Box::new(EmLinker { - cmd, - sess, - info: self - }) as Box - } - LinkerFlavor::Gcc => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: false, - }) as Box - } - - LinkerFlavor::Lld(LldFlavor::Ld) | - LinkerFlavor::Lld(LldFlavor::Ld64) | - LinkerFlavor::Ld => { - Box::new(GccLinker { - cmd, - sess, - info: self, - hinted_static: false, - is_ld: true, - }) as Box - } - - LinkerFlavor::Lld(LldFlavor::Wasm) => { - Box::new(WasmLd { - cmd, - }) as Box - } - } - } -} - -/// Linker abstraction used by back::link to build up the command to invoke a -/// linker. -/// -/// This trait is the total list of requirements needed by `back::link` and -/// represents the meaning of each option being passed down. This trait is then -/// used to dispatch on whether a GNU-like linker (generally `ld.exe`) or an -/// MSVC linker (e.g. `link.exe`) is being used. -pub trait Linker { - fn link_dylib(&mut self, lib: &str); - fn link_rust_dylib(&mut self, lib: &str, path: &Path); - fn link_framework(&mut self, framework: &str); - fn link_staticlib(&mut self, lib: &str); - fn link_rlib(&mut self, lib: &Path); - fn link_whole_rlib(&mut self, lib: &Path); - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]); - fn include_path(&mut self, path: &Path); - fn framework_path(&mut self, path: &Path); - fn output_filename(&mut self, path: &Path); - fn add_object(&mut self, path: &Path); - fn gc_sections(&mut self, keep_metadata: bool); - fn position_independent_executable(&mut self); - fn no_position_independent_executable(&mut self); - fn full_relro(&mut self); - fn partial_relro(&mut self); - fn no_relro(&mut self); - fn optimize(&mut self); - fn pgo_gen(&mut self); - fn debuginfo(&mut self); - fn no_default_libraries(&mut self); - fn build_dylib(&mut self, out_filename: &Path); - fn build_static_executable(&mut self); - fn args(&mut self, args: &[String]); - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType); - fn subsystem(&mut self, subsystem: &str); - fn group_start(&mut self); - fn group_end(&mut self); - fn cross_lang_lto(&mut self); - // Should have been finalize(self), but we don't support self-by-value on trait objects (yet?). - fn finalize(&mut self) -> Command; -} - -pub struct GccLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo, - hinted_static: bool, // Keeps track of the current hinting mode. - // Link as ld - is_ld: bool, -} - -impl<'a> GccLinker<'a> { - /// Argument that must be passed *directly* to the linker - /// - /// These arguments need to be prepended with '-Wl,' when a gcc-style linker is used - fn linker_arg(&mut self, arg: S) -> &mut Self - where S: AsRef - { - if !self.is_ld { - let mut os = OsString::from("-Wl,"); - os.push(arg.as_ref()); - self.cmd.arg(os); - } else { - self.cmd.arg(arg); - } - self - } - - fn takes_hints(&self) -> bool { - !self.sess.target.target.options.is_like_osx - } - - // Some platforms take hints about whether a library is static or dynamic. - // For those that support this, we ensure we pass the option if the library - // was flagged "static" (most defaults are dynamic) to ensure that if - // libfoo.a and libfoo.so both exist that the right one is chosen. - fn hint_static(&mut self) { - if !self.takes_hints() { return } - if !self.hinted_static { - self.linker_arg("-Bstatic"); - self.hinted_static = true; - } - } - - fn hint_dynamic(&mut self) { - if !self.takes_hints() { return } - if self.hinted_static { - self.linker_arg("-Bdynamic"); - self.hinted_static = false; - } - } -} - -impl<'a> Linker for GccLinker<'a> { - fn link_dylib(&mut self, lib: &str) { self.hint_dynamic(); self.cmd.arg("-l").arg(lib); } - fn link_staticlib(&mut self, lib: &str) { self.hint_static(); self.cmd.arg("-l").arg(lib); } - fn link_rlib(&mut self, lib: &Path) { self.hint_static(); self.cmd.arg(lib); } - fn include_path(&mut self, path: &Path) { self.cmd.arg("-L").arg(path); } - fn framework_path(&mut self, path: &Path) { self.cmd.arg("-F").arg(path); } - fn output_filename(&mut self, path: &Path) { self.cmd.arg("-o").arg(path); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn position_independent_executable(&mut self) { self.cmd.arg("-pie"); } - fn no_position_independent_executable(&mut self) { self.cmd.arg("-no-pie"); } - fn full_relro(&mut self) { self.linker_arg("-z,relro,-z,now"); } - fn partial_relro(&mut self) { self.linker_arg("-z,relro"); } - fn no_relro(&mut self) { self.linker_arg("-z,norelro"); } - fn build_static_executable(&mut self) { self.cmd.arg("-static"); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.hint_dynamic(); - self.cmd.arg("-l").arg(lib); - } - - fn link_framework(&mut self, framework: &str) { - self.hint_dynamic(); - self.cmd.arg("-framework").arg(framework); - } - - // Here we explicitly ask that the entire archive is included into the - // result artifact. For more details see #15460, but the gist is that - // the linker will strip away any unused objects in the archive if we - // don't otherwise explicitly reference them. This can occur for - // libraries which are just providing bindings, libraries with generic - // functions, etc. - fn link_whole_staticlib(&mut self, lib: &str, search_path: &[PathBuf]) { - self.hint_static(); - let target = &self.sess.target.target; - if !target.options.is_like_osx { - self.linker_arg("--whole-archive").cmd.arg("-l").arg(lib); - self.linker_arg("--no-whole-archive"); - } else { - // -force_load is the macOS equivalent of --whole-archive, but it - // involves passing the full path to the library to link. - let mut v = OsString::from("-force_load,"); - v.push(&archive::find_library(lib, search_path, &self.sess)); - self.linker_arg(&v); - } - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.hint_static(); - if self.sess.target.target.options.is_like_osx { - let mut v = OsString::from("-force_load,"); - v.push(lib); - self.linker_arg(&v); - } else { - self.linker_arg("--whole-archive").cmd.arg(lib); - self.linker_arg("--no-whole-archive"); - } - } - - fn gc_sections(&mut self, keep_metadata: bool) { - // The dead_strip option to the linker specifies that functions and data - // unreachable by the entry point will be removed. This is quite useful - // with Rust's compilation model of compiling libraries at a time into - // one object file. For example, this brings hello world from 1.7MB to - // 458K. - // - // Note that this is done for both executables and dynamic libraries. We - // won't get much benefit from dylibs because LLVM will have already - // stripped away as much as it could. This has not been seen to impact - // link times negatively. - // - // -dead_strip can't be part of the pre_link_args because it's also used - // for partial linking when using multiple codegen units (-r). So we - // insert it here. - if self.sess.target.target.options.is_like_osx { - self.linker_arg("-dead_strip"); - } else if self.sess.target.target.options.is_like_solaris { - self.linker_arg("-z"); - self.linker_arg("ignore"); - - // If we're building a dylib, we don't use --gc-sections because LLVM - // has already done the best it can do, and we also don't want to - // eliminate the metadata. If we're building an executable, however, - // --gc-sections drops the size of hello world from 1.8MB to 597K, a 67% - // reduction. - } else if !keep_metadata { - self.linker_arg("--gc-sections"); - } - } - - fn optimize(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // GNU-style linkers support optimization with -O. GNU ld doesn't - // need a numeric argument, but other linkers do. - if self.sess.opts.optimize == config::OptLevel::Default || - self.sess.opts.optimize == config::OptLevel::Aggressive { - self.linker_arg("-O1"); - } - } - - fn pgo_gen(&mut self) { - if !self.sess.target.target.options.linker_is_gnu { return } - - // If we're doing PGO generation stuff and on a GNU-like linker, use the - // "-u" flag to properly pull in the profiler runtime bits. - // - // This is because LLVM otherwise won't add the needed initialization - // for us on Linux (though the extra flag should be harmless if it - // does). - // - // See https://reviews.llvm.org/D14033 and https://reviews.llvm.org/D14030. - // - // Though it may be worth to try to revert those changes upstream, since - // the overhead of the initialization should be minor. - self.cmd.arg("-u"); - self.cmd.arg("__llvm_profile_runtime"); - } - - fn debuginfo(&mut self) { - match self.sess.opts.debuginfo { - DebugInfoLevel::NoDebugInfo => { - // If we are building without debuginfo enabled and we were called with - // `-Zstrip-debuginfo-if-disabled=yes`, tell the linker to strip any debuginfo - // found when linking to get rid of symbols from libstd. - match self.sess.opts.debugging_opts.strip_debuginfo_if_disabled { - Some(true) => { self.linker_arg("-S"); }, - _ => {}, - } - }, - _ => {}, - }; - } - - fn no_default_libraries(&mut self) { - if !self.is_ld { - self.cmd.arg("-nodefaultlibs"); - } - } - - fn build_dylib(&mut self, out_filename: &Path) { - // On mac we need to tell the linker to let this library be rpathed - if self.sess.target.target.options.is_like_osx { - self.cmd.arg("-dynamiclib"); - self.linker_arg("-dylib"); - - // Note that the `osx_rpath_install_name` option here is a hack - // purely to support rustbuild right now, we should get a more - // principled solution at some point to force the compiler to pass - // the right `-Wl,-install_name` with an `@rpath` in it. - if self.sess.opts.cg.rpath || - self.sess.opts.debugging_opts.osx_rpath_install_name { - let mut v = OsString::from("-install_name,@rpath/"); - v.push(out_filename.file_name().unwrap()); - self.linker_arg(&v); - } - } else { - self.cmd.arg("-shared"); - } - } - - fn export_symbols(&mut self, tmpdir: &Path, crate_type: CrateType) { - // If we're compiling a dylib, then we let symbol visibility in object - // files to take care of whether they're exported or not. - // - // If we're compiling a cdylib, however, we manually create a list of - // exported symbols to ensure we don't expose any more. The object files - // have far more public symbols than we actually want to export, so we - // hide them all here. - if crate_type == CrateType::CrateTypeDylib || - crate_type == CrateType::CrateTypeProcMacro { - return - } - - let mut arg = OsString::new(); - let path = tmpdir.join("list"); - - debug!("EXPORTED SYMBOLS:"); - - if self.sess.target.target.options.is_like_osx { - // Write a plain, newline-separated list of symbols - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - for sym in self.info.exports[&crate_type].iter() { - debug!(" _{}", sym); - writeln!(f, "_{}", sym)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - } else { - // Write an LD version script - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - writeln!(f, "{{\n global:")?; - for sym in self.info.exports[&crate_type].iter() { - debug!(" {};", sym); - writeln!(f, " {};", sym)?; - } - writeln!(f, "\n local:\n *;\n}};")?; - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write version script: {}", e)); - } - } - - if self.sess.target.target.options.is_like_osx { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-exported_symbols_list,"); - } else if self.sess.target.target.options.is_like_solaris { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("-M,"); - } else { - if !self.is_ld { - arg.push("-Wl,") - } - arg.push("--version-script="); - } - - arg.push(&path); - self.cmd.arg(arg); - } - - fn subsystem(&mut self, subsystem: &str) { - self.linker_arg(&format!("--subsystem,{}", subsystem)); - } - - fn finalize(&mut self) -> Command { - self.hint_dynamic(); // Reset to default before returning the composed command line. - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - fn group_start(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--start-group"); - } - } - - fn group_end(&mut self) { - if !self.sess.target.target.options.is_like_osx { - self.linker_arg("--end-group"); - } - } - - fn cross_lang_lto(&mut self) { - match self.sess.opts.debugging_opts.cross_lang_lto { - CrossLangLto::Disabled | - CrossLangLto::NoLink => { - // Nothing to do - } - CrossLangLto::LinkerPlugin(ref path) => { - self.linker_arg(&format!("-plugin={}", path.display())); - - let opt_level = match self.sess.opts.optimize { - config::OptLevel::No => "O0", - config::OptLevel::Less => "O1", - config::OptLevel::Default => "O2", - config::OptLevel::Aggressive => "O3", - config::OptLevel::Size => "Os", - config::OptLevel::SizeMin => "Oz", - }; - - self.linker_arg(&format!("-plugin-opt={}", opt_level)); - self.linker_arg(&format!("-plugin-opt=mcpu={}", self.sess.target_cpu())); - - match self.sess.opts.cg.lto { - config::Lto::Thin | - config::Lto::ThinLocal => { - self.linker_arg(&format!("-plugin-opt=thin")); - } - config::Lto::Fat | - config::Lto::Yes | - config::Lto::No => { - // default to regular LTO - } - } - } - } - } -} - -pub struct MsvcLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for MsvcLinker<'a> { - fn link_rlib(&mut self, lib: &Path) { self.cmd.arg(lib); } - fn add_object(&mut self, path: &Path) { self.cmd.arg(path); } - fn args(&mut self, args: &[String]) { self.cmd.args(args); } - - fn build_dylib(&mut self, out_filename: &Path) { - self.cmd.arg("/DLL"); - let mut arg: OsString = "/IMPLIB:".into(); - arg.push(out_filename.with_extension("dll.lib")); - self.cmd.arg(arg); - } - - fn build_static_executable(&mut self) { - // noop - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // MSVC's ICF (Identical COMDAT Folding) link optimization is - // slow for Rust and thus we disable it by default when not in - // optimization build. - if self.sess.opts.optimize != config::OptLevel::No { - self.cmd.arg("/OPT:REF,ICF"); - } else { - // It is necessary to specify NOICF here, because /OPT:REF - // implies ICF by default. - self.cmd.arg("/OPT:REF,NOICF"); - } - } - - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn link_rust_dylib(&mut self, lib: &str, path: &Path) { - // When producing a dll, the MSVC linker may not actually emit a - // `foo.lib` file if the dll doesn't actually export any symbols, so we - // check to see if the file is there and just omit linking to it if it's - // not present. - let name = format!("{}.dll.lib", lib); - if fs::metadata(&path.join(&name)).is_ok() { - self.cmd.arg(name); - } - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg(&format!("{}.lib", lib)); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn no_default_libraries(&mut self) { - // Currently we don't pass the /NODEFAULTLIB flag to the linker on MSVC - // as there's been trouble in the past of linking the C++ standard - // library required by LLVM. This likely needs to happen one day, but - // in general Windows is also a more controlled environment than - // Unix, so it's not necessarily as critical that this be implemented. - // - // Note that there are also some licensing worries about statically - // linking some libraries which require a specific agreement, so it may - // not ever be possible for us to pass this flag. - } - - fn include_path(&mut self, path: &Path) { - let mut arg = OsString::from("/LIBPATH:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn output_filename(&mut self, path: &Path) { - let mut arg = OsString::from("/OUT:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on windows") - } - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on windows") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - fn link_whole_rlib(&mut self, path: &Path) { - // not supported? - self.link_rlib(path); - } - fn optimize(&mut self) { - // Needs more investigation of `/OPT` arguments - } - - fn pgo_gen(&mut self) { - // Nothing needed here. - } - - fn debuginfo(&mut self) { - // This will cause the Microsoft linker to generate a PDB file - // from the CodeView line tables in the object files. - self.cmd.arg("/DEBUG"); - - // This will cause the Microsoft linker to embed .natvis info into the the PDB file - let sysroot = self.sess.sysroot(); - let natvis_dir_path = sysroot.join("lib\\rustlib\\etc"); - if let Ok(natvis_dir) = fs::read_dir(&natvis_dir_path) { - // LLVM 5.0.0's lld-link frontend doesn't yet recognize, and chokes - // on, the /NATVIS:... flags. LLVM 6 (or earlier) should at worst ignore - // them, eventually mooting this workaround, per this landed patch: - // https://github.com/llvm-mirror/lld/commit/27b9c4285364d8d76bb43839daa100 - if let Some(ref linker_path) = self.sess.opts.cg.linker { - if let Some(linker_name) = Path::new(&linker_path).file_stem() { - if linker_name.to_str().unwrap().to_lowercase() == "lld-link" { - self.sess.warn("not embedding natvis: lld-link may not support the flag"); - return; - } - } - } - for entry in natvis_dir { - match entry { - Ok(entry) => { - let path = entry.path(); - if path.extension() == Some("natvis".as_ref()) { - let mut arg = OsString::from("/NATVIS:"); - arg.push(path); - self.cmd.arg(arg); - } - }, - Err(err) => { - self.sess.warn(&format!("error enumerating natvis directory: {}", err)); - }, - } - } - } - } - - // Currently the compiler doesn't use `dllexport` (an LLVM attribute) to - // export symbols from a dynamic library. When building a dynamic library, - // however, we're going to want some symbols exported, so this function - // generates a DEF file which lists all the symbols. - // - // The linker will read this `*.def` file and export all the symbols from - // the dynamic library. Note that this is not as simple as just exporting - // all the symbols in the current crate (as specified by `trans.reachable`) - // but rather we also need to possibly export the symbols of upstream - // crates. Upstream rlibs may be linked statically to this dynamic library, - // in which case they may continue to transitively be used and hence need - // their symbols exported. - fn export_symbols(&mut self, - tmpdir: &Path, - crate_type: CrateType) { - let path = tmpdir.join("lib.def"); - let res = (|| -> io::Result<()> { - let mut f = BufWriter::new(File::create(&path)?); - - // Start off with the standard module name header and then go - // straight to exports. - writeln!(f, "LIBRARY")?; - writeln!(f, "EXPORTS")?; - for symbol in self.info.exports[&crate_type].iter() { - debug!(" _{}", symbol); - writeln!(f, " {}", symbol)?; - } - Ok(()) - })(); - if let Err(e) = res { - self.sess.fatal(&format!("failed to write lib.def file: {}", e)); - } - let mut arg = OsString::from("/DEF:"); - arg.push(path); - self.cmd.arg(&arg); - } - - fn subsystem(&mut self, subsystem: &str) { - // Note that previous passes of the compiler validated this subsystem, - // so we just blindly pass it to the linker. - self.cmd.arg(&format!("/SUBSYSTEM:{}", subsystem)); - - // Windows has two subsystems we're interested in right now, the console - // and windows subsystems. These both implicitly have different entry - // points (starting symbols). The console entry point starts with - // `mainCRTStartup` and the windows entry point starts with - // `WinMainCRTStartup`. These entry points, defined in system libraries, - // will then later probe for either `main` or `WinMain`, respectively to - // start the application. - // - // In Rust we just always generate a `main` function so we want control - // to always start there, so we force the entry point on the windows - // subsystem to be `mainCRTStartup` to get everything booted up - // correctly. - // - // For more information see RFC #1665 - if subsystem == "windows" { - self.cmd.arg("/ENTRY:mainCRTStartup"); - } - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // MSVC doesn't need group indicators - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -pub struct EmLinker<'a> { - cmd: Command, - sess: &'a Session, - info: &'a LinkerInfo -} - -impl<'a> Linker for EmLinker<'a> { - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn link_dylib(&mut self, lib: &str) { - // Emscripten always links statically - self.link_staticlib(lib); - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - // not supported? - self.link_staticlib(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - // not supported? - self.link_rlib(lib); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.link_dylib(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.add_object(lib); - } - - fn position_independent_executable(&mut self) { - // noop - } - - fn no_position_independent_executable(&mut self) { - // noop - } - - fn full_relro(&mut self) { - // noop - } - - fn partial_relro(&mut self) { - // noop - } - - fn no_relro(&mut self) { - // noop - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn framework_path(&mut self, _path: &Path) { - bug!("frameworks are not supported on Emscripten") - } - - fn link_framework(&mut self, _framework: &str) { - bug!("frameworks are not supported on Emscripten") - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - // noop - } - - fn optimize(&mut self) { - // Emscripten performs own optimizations - self.cmd.arg(match self.sess.opts.optimize { - OptLevel::No => "-O0", - OptLevel::Less => "-O1", - OptLevel::Default => "-O2", - OptLevel::Aggressive => "-O3", - OptLevel::Size => "-Os", - OptLevel::SizeMin => "-Oz" - }); - // Unusable until https://github.com/rust-lang/rust/issues/38454 is resolved - self.cmd.args(&["--memory-init-file", "0"]); - } - - fn pgo_gen(&mut self) { - // noop, but maybe we need something like the gnu linker? - } - - fn debuginfo(&mut self) { - // Preserve names or generate source maps depending on debug info - self.cmd.arg(match self.sess.opts.debuginfo { - DebugInfoLevel::NoDebugInfo => "-g0", - DebugInfoLevel::LimitedDebugInfo => "-g3", - DebugInfoLevel::FullDebugInfo => "-g4" - }); - } - - fn no_default_libraries(&mut self) { - self.cmd.args(&["-s", "DEFAULT_LIBRARY_FUNCS_TO_INCLUDE=[]"]); - } - - fn build_dylib(&mut self, _out_filename: &Path) { - bug!("building dynamic library is unsupported on Emscripten") - } - - fn build_static_executable(&mut self) { - // noop - } - - fn export_symbols(&mut self, _tmpdir: &Path, crate_type: CrateType) { - let symbols = &self.info.exports[&crate_type]; - - debug!("EXPORTED SYMBOLS:"); - - self.cmd.arg("-s"); - - let mut arg = OsString::from("EXPORTED_FUNCTIONS="); - let mut encoded = String::new(); - - { - let mut encoder = json::Encoder::new(&mut encoded); - let res = encoder.emit_seq(symbols.len(), |encoder| { - for (i, sym) in symbols.iter().enumerate() { - encoder.emit_seq_elt(i, |encoder| { - encoder.emit_str(&("_".to_string() + sym)) - })?; - } - Ok(()) - }); - if let Err(e) = res { - self.sess.fatal(&format!("failed to encode exported symbols: {}", e)); - } - } - debug!("{}", encoded); - arg.push(encoded); - - self.cmd.arg(arg); - } - - fn subsystem(&mut self, _subsystem: &str) { - // noop - } - - fn finalize(&mut self) -> Command { - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Appears not necessary on Emscripten - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing - } -} - -fn exported_symbols(tcx: TyCtxt, crate_type: CrateType) -> Vec { - let mut symbols = Vec::new(); - - let export_threshold = symbol_export::crates_export_threshold(&[crate_type]); - for &(symbol, level) in tcx.exported_symbols(LOCAL_CRATE).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - - let formats = tcx.sess.dependency_formats.borrow(); - let deps = formats[&crate_type].iter(); - - for (index, dep_format) in deps.enumerate() { - let cnum = CrateNum::new(index + 1); - // For each dependency that we are linking to statically ... - if *dep_format == Linkage::Static { - // ... we add its symbol list to our export list. - for &(symbol, level) in tcx.exported_symbols(cnum).iter() { - if level.is_below_threshold(export_threshold) { - symbols.push(symbol.symbol_name(tcx).to_string()); - } - } - } - } - - symbols -} - -pub struct WasmLd { - cmd: Command, -} - -impl Linker for WasmLd { - fn link_dylib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_staticlib(&mut self, lib: &str) { - self.cmd.arg("-l").arg(lib); - } - - fn link_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn include_path(&mut self, path: &Path) { - self.cmd.arg("-L").arg(path); - } - - fn framework_path(&mut self, _path: &Path) { - panic!("frameworks not supported") - } - - fn output_filename(&mut self, path: &Path) { - self.cmd.arg("-o").arg(path); - } - - fn add_object(&mut self, path: &Path) { - self.cmd.arg(path); - } - - fn position_independent_executable(&mut self) { - } - - fn full_relro(&mut self) { - } - - fn partial_relro(&mut self) { - } - - fn no_relro(&mut self) { - } - - fn build_static_executable(&mut self) { - } - - fn args(&mut self, args: &[String]) { - self.cmd.args(args); - } - - fn link_rust_dylib(&mut self, lib: &str, _path: &Path) { - self.cmd.arg("-l").arg(lib); - } - - fn link_framework(&mut self, _framework: &str) { - panic!("frameworks not supported") - } - - fn link_whole_staticlib(&mut self, lib: &str, _search_path: &[PathBuf]) { - self.cmd.arg("-l").arg(lib); - } - - fn link_whole_rlib(&mut self, lib: &Path) { - self.cmd.arg(lib); - } - - fn gc_sections(&mut self, _keep_metadata: bool) { - } - - fn optimize(&mut self) { - } - - fn pgo_gen(&mut self) { - } - - fn debuginfo(&mut self) { - } - - fn no_default_libraries(&mut self) { - } - - fn build_dylib(&mut self, _out_filename: &Path) { - } - - fn export_symbols(&mut self, _tmpdir: &Path, _crate_type: CrateType) { - } - - fn subsystem(&mut self, _subsystem: &str) { - } - - fn no_position_independent_executable(&mut self) { - } - - fn finalize(&mut self) -> Command { - // There have been reports in the wild (rustwasm/wasm-bindgen#119) of - // using threads causing weird hangs and bugs. Disable it entirely as - // this isn't yet the bottleneck of compilation at all anyway. - self.cmd.arg("--no-threads"); - - self.cmd.arg("-z").arg("stack-size=1048576"); - - // FIXME we probably shouldn't pass this but instead pass an explicit - // whitelist of symbols we'll allow to be undefined. Unfortunately - // though we can't handle symbols like `log10` that LLVM injects at a - // super late date without actually parsing object files. For now let's - // stick to this and hopefully fix it before stabilization happens. - self.cmd.arg("--allow-undefined"); - - // For now we just never have an entry symbol - self.cmd.arg("--no-entry"); - - let mut cmd = Command::new(""); - ::std::mem::swap(&mut cmd, &mut self.cmd); - cmd - } - - // Not needed for now with LLD - fn group_start(&mut self) {} - fn group_end(&mut self) {} - - fn cross_lang_lto(&mut self) { - // Do nothing for now - } -} diff --git a/src/librustc_trans/back/lto.rs b/src/librustc_trans/back/lto.rs deleted file mode 100644 index bbb5f7eecc8..00000000000 --- a/src/librustc_trans/back/lto.rs +++ /dev/null @@ -1,773 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use back::bytecode::{DecodedBytecode, RLIB_BYTECODE_EXTENSION}; -use back::symbol_export; -use back::write::{ModuleConfig, with_llvm_pmb, CodegenContext}; -use back::write; -use errors::{FatalError, Handler}; -use llvm::archive_ro::ArchiveRO; -use llvm::{ModuleRef, TargetMachineRef, True, False}; -use llvm; -use rustc::hir::def_id::LOCAL_CRATE; -use rustc::middle::exported_symbols::SymbolExportLevel; -use rustc::session::config::{self, Lto}; -use rustc::util::common::time_ext; -use time_graph::Timeline; -use {ModuleTranslation, ModuleLlvm, ModuleKind, ModuleSource}; - -use libc; - -use std::ffi::CString; -use std::ptr; -use std::slice; -use std::sync::Arc; - -pub fn crate_type_allows_lto(crate_type: config::CrateType) -> bool { - match crate_type { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeCdylib => true, - - config::CrateTypeDylib | - config::CrateTypeRlib | - config::CrateTypeProcMacro => false, - } -} - -pub(crate) enum LtoModuleTranslation { - Fat { - module: Option, - _serialized_bitcode: Vec, - }, - - Thin(ThinModule), -} - -impl LtoModuleTranslation { - pub fn name(&self) -> &str { - match *self { - LtoModuleTranslation::Fat { .. } => "everything", - LtoModuleTranslation::Thin(ref m) => m.name(), - } - } - - /// Optimize this module within the given codegen context. - /// - /// This function is unsafe as it'll return a `ModuleTranslation` still - /// points to LLVM data structures owned by this `LtoModuleTranslation`. - /// It's intended that the module returned is immediately code generated and - /// dropped, and then this LTO module is dropped. - pub(crate) unsafe fn optimize(&mut self, - cgcx: &CodegenContext, - timeline: &mut Timeline) - -> Result - { - match *self { - LtoModuleTranslation::Fat { ref mut module, .. } => { - let trans = module.take().unwrap(); - let config = cgcx.config(trans.kind); - let llmod = trans.llvm().unwrap().llmod; - let tm = trans.llvm().unwrap().tm; - run_pass_manager(cgcx, tm, llmod, config, false); - timeline.record("fat-done"); - Ok(trans) - } - LtoModuleTranslation::Thin(ref mut thin) => thin.optimize(cgcx, timeline), - } - } - - /// A "gauge" of how costly it is to optimize this module, used to sort - /// biggest modules first. - pub fn cost(&self) -> u64 { - match *self { - // Only one module with fat LTO, so the cost doesn't matter. - LtoModuleTranslation::Fat { .. } => 0, - LtoModuleTranslation::Thin(ref m) => m.cost(), - } - } -} - -pub(crate) fn run(cgcx: &CodegenContext, - modules: Vec, - timeline: &mut Timeline) - -> Result, FatalError> -{ - let diag_handler = cgcx.create_diag_handler(); - let export_threshold = match cgcx.lto { - // We're just doing LTO for our one crate - Lto::ThinLocal => SymbolExportLevel::Rust, - - // We're doing LTO for the entire crate graph - Lto::Yes | Lto::Fat | Lto::Thin => { - symbol_export::crates_export_threshold(&cgcx.crate_types) - } - - Lto::No => panic!("didn't request LTO but we're doing LTO"), - }; - - let symbol_filter = &|&(ref name, level): &(String, SymbolExportLevel)| { - if level.is_below_threshold(export_threshold) { - let mut bytes = Vec::with_capacity(name.len() + 1); - bytes.extend(name.bytes()); - Some(CString::new(bytes).unwrap()) - } else { - None - } - }; - let exported_symbols = cgcx.exported_symbols - .as_ref().expect("needs exported symbols for LTO"); - let mut symbol_white_list = exported_symbols[&LOCAL_CRATE] - .iter() - .filter_map(symbol_filter) - .collect::>(); - timeline.record("whitelist"); - info!("{} symbols to preserve in this crate", symbol_white_list.len()); - - // If we're performing LTO for the entire crate graph, then for each of our - // upstream dependencies, find the corresponding rlib and load the bitcode - // from the archive. - // - // We save off all the bytecode and LLVM module ids for later processing - // with either fat or thin LTO - let mut upstream_modules = Vec::new(); - if cgcx.lto != Lto::ThinLocal { - if cgcx.opts.cg.prefer_dynamic { - diag_handler.struct_err("cannot prefer dynamic linking when performing LTO") - .note("only 'staticlib', 'bin', and 'cdylib' outputs are \ - supported with LTO") - .emit(); - return Err(FatalError) - } - - // Make sure we actually can run LTO - for crate_type in cgcx.crate_types.iter() { - if !crate_type_allows_lto(*crate_type) { - let e = diag_handler.fatal("lto can only be run for executables, cdylibs and \ - static library outputs"); - return Err(e) - } - } - - for &(cnum, ref path) in cgcx.each_linked_rlib_for_lto.iter() { - let exported_symbols = cgcx.exported_symbols - .as_ref().expect("needs exported symbols for LTO"); - symbol_white_list.extend( - exported_symbols[&cnum] - .iter() - .filter_map(symbol_filter)); - - let archive = ArchiveRO::open(&path).expect("wanted an rlib"); - let bytecodes = archive.iter().filter_map(|child| { - child.ok().and_then(|c| c.name().map(|name| (name, c))) - }).filter(|&(name, _)| name.ends_with(RLIB_BYTECODE_EXTENSION)); - for (name, data) in bytecodes { - info!("adding bytecode {}", name); - let bc_encoded = data.data(); - - let (bc, id) = time_ext(cgcx.time_passes, None, &format!("decode {}", name), || { - match DecodedBytecode::new(bc_encoded) { - Ok(b) => Ok((b.bytecode(), b.identifier().to_string())), - Err(e) => Err(diag_handler.fatal(&e)), - } - })?; - let bc = SerializedModule::FromRlib(bc); - upstream_modules.push((bc, CString::new(id).unwrap())); - } - timeline.record(&format!("load: {}", path.display())); - } - } - - let arr = symbol_white_list.iter().map(|c| c.as_ptr()).collect::>(); - match cgcx.lto { - Lto::Yes | // `-C lto` == fat LTO by default - Lto::Fat => { - fat_lto(cgcx, &diag_handler, modules, upstream_modules, &arr, timeline) - } - Lto::Thin | - Lto::ThinLocal => { - thin_lto(&diag_handler, modules, upstream_modules, &arr, timeline) - } - Lto::No => unreachable!(), - } -} - -fn fat_lto(cgcx: &CodegenContext, - diag_handler: &Handler, - mut modules: Vec, - mut serialized_modules: Vec<(SerializedModule, CString)>, - symbol_white_list: &[*const libc::c_char], - timeline: &mut Timeline) - -> Result, FatalError> -{ - info!("going for a fat lto"); - - // Find the "costliest" module and merge everything into that codegen unit. - // All the other modules will be serialized and reparsed into the new - // context, so this hopefully avoids serializing and parsing the largest - // codegen unit. - // - // Additionally use a regular module as the base here to ensure that various - // file copy operations in the backend work correctly. The only other kind - // of module here should be an allocator one, and if your crate is smaller - // than the allocator module then the size doesn't really matter anyway. - let (_, costliest_module) = modules.iter() - .enumerate() - .filter(|&(_, module)| module.kind == ModuleKind::Regular) - .map(|(i, module)| { - let cost = unsafe { - llvm::LLVMRustModuleCost(module.llvm().unwrap().llmod) - }; - (cost, i) - }) - .max() - .expect("must be trans'ing at least one module"); - let module = modules.remove(costliest_module); - let llmod = module.llvm().expect("can't lto pre-translated modules").llmod; - info!("using {:?} as a base module", module.llmod_id); - - // For all other modules we translated we'll need to link them into our own - // bitcode. All modules were translated in their own LLVM context, however, - // and we want to move everything to the same LLVM context. Currently the - // way we know of to do that is to serialize them to a string and them parse - // them later. Not great but hey, that's why it's "fat" LTO, right? - for module in modules { - let llvm = module.llvm().expect("can't lto pre-translated modules"); - let buffer = ModuleBuffer::new(llvm.llmod); - let llmod_id = CString::new(&module.llmod_id[..]).unwrap(); - serialized_modules.push((SerializedModule::Local(buffer), llmod_id)); - } - - // For all serialized bitcode files we parse them and link them in as we did - // above, this is all mostly handled in C++. Like above, though, we don't - // know much about the memory management here so we err on the side of being - // save and persist everything with the original module. - let mut serialized_bitcode = Vec::new(); - let mut linker = Linker::new(llmod); - for (bc_decoded, name) in serialized_modules { - info!("linking {:?}", name); - time_ext(cgcx.time_passes, None, &format!("ll link {:?}", name), || { - let data = bc_decoded.data(); - linker.add(&data).map_err(|()| { - let msg = format!("failed to load bc of {:?}", name); - write::llvm_err(&diag_handler, msg) - }) - })?; - timeline.record(&format!("link {:?}", name)); - serialized_bitcode.push(bc_decoded); - } - drop(linker); - cgcx.save_temp_bitcode(&module, "lto.input"); - - // Internalize everything that *isn't* in our whitelist to help strip out - // more modules and such - unsafe { - let ptr = symbol_white_list.as_ptr(); - llvm::LLVMRustRunRestrictionPass(llmod, - ptr as *const *const libc::c_char, - symbol_white_list.len() as libc::size_t); - cgcx.save_temp_bitcode(&module, "lto.after-restriction"); - } - - if cgcx.no_landing_pads { - unsafe { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - } - cgcx.save_temp_bitcode(&module, "lto.after-nounwind"); - } - timeline.record("passes"); - - Ok(vec![LtoModuleTranslation::Fat { - module: Some(module), - _serialized_bitcode: serialized_bitcode, - }]) -} - -struct Linker(llvm::LinkerRef); - -impl Linker { - fn new(llmod: ModuleRef) -> Linker { - unsafe { Linker(llvm::LLVMRustLinkerNew(llmod)) } - } - - fn add(&mut self, bytecode: &[u8]) -> Result<(), ()> { - unsafe { - if llvm::LLVMRustLinkerAdd(self.0, - bytecode.as_ptr() as *const libc::c_char, - bytecode.len()) { - Ok(()) - } else { - Err(()) - } - } - } -} - -impl Drop for Linker { - fn drop(&mut self) { - unsafe { llvm::LLVMRustLinkerFree(self.0); } - } -} - -/// Prepare "thin" LTO to get run on these modules. -/// -/// The general structure of ThinLTO is quite different from the structure of -/// "fat" LTO above. With "fat" LTO all LLVM modules in question are merged into -/// one giant LLVM module, and then we run more optimization passes over this -/// big module after internalizing most symbols. Thin LTO, on the other hand, -/// avoid this large bottleneck through more targeted optimization. -/// -/// At a high level Thin LTO looks like: -/// -/// 1. Prepare a "summary" of each LLVM module in question which describes -/// the values inside, cost of the values, etc. -/// 2. Merge the summaries of all modules in question into one "index" -/// 3. Perform some global analysis on this index -/// 4. For each module, use the index and analysis calculated previously to -/// perform local transformations on the module, for example inlining -/// small functions from other modules. -/// 5. Run thin-specific optimization passes over each module, and then code -/// generate everything at the end. -/// -/// The summary for each module is intended to be quite cheap, and the global -/// index is relatively quite cheap to create as well. As a result, the goal of -/// ThinLTO is to reduce the bottleneck on LTO and enable LTO to be used in more -/// situations. For example one cheap optimization is that we can parallelize -/// all codegen modules, easily making use of all the cores on a machine. -/// -/// With all that in mind, the function here is designed at specifically just -/// calculating the *index* for ThinLTO. This index will then be shared amongst -/// all of the `LtoModuleTranslation` units returned below and destroyed once -/// they all go out of scope. -fn thin_lto(diag_handler: &Handler, - modules: Vec, - serialized_modules: Vec<(SerializedModule, CString)>, - symbol_white_list: &[*const libc::c_char], - timeline: &mut Timeline) - -> Result, FatalError> -{ - unsafe { - info!("going for that thin, thin LTO"); - - let mut thin_buffers = Vec::new(); - let mut module_names = Vec::new(); - let mut thin_modules = Vec::new(); - - // FIXME: right now, like with fat LTO, we serialize all in-memory - // modules before working with them and ThinLTO. We really - // shouldn't do this, however, and instead figure out how to - // extract a summary from an in-memory module and then merge that - // into the global index. It turns out that this loop is by far - // the most expensive portion of this small bit of global - // analysis! - for (i, module) in modules.iter().enumerate() { - info!("local module: {} - {}", i, module.llmod_id); - let llvm = module.llvm().expect("can't lto pretranslated module"); - let name = CString::new(module.llmod_id.clone()).unwrap(); - let buffer = ThinBuffer::new(llvm.llmod); - thin_modules.push(llvm::ThinLTOModule { - identifier: name.as_ptr(), - data: buffer.data().as_ptr(), - len: buffer.data().len(), - }); - thin_buffers.push(buffer); - module_names.push(name); - timeline.record(&module.llmod_id); - } - - // FIXME: All upstream crates are deserialized internally in the - // function below to extract their summary and modules. Note that - // unlike the loop above we *must* decode and/or read something - // here as these are all just serialized files on disk. An - // improvement, however, to make here would be to store the - // module summary separately from the actual module itself. Right - // now this is store in one large bitcode file, and the entire - // file is deflate-compressed. We could try to bypass some of the - // decompression by storing the index uncompressed and only - // lazily decompressing the bytecode if necessary. - // - // Note that truly taking advantage of this optimization will - // likely be further down the road. We'd have to implement - // incremental ThinLTO first where we could actually avoid - // looking at upstream modules entirely sometimes (the contents, - // we must always unconditionally look at the index). - let mut serialized = Vec::new(); - for (module, name) in serialized_modules { - info!("foreign module {:?}", name); - thin_modules.push(llvm::ThinLTOModule { - identifier: name.as_ptr(), - data: module.data().as_ptr(), - len: module.data().len(), - }); - serialized.push(module); - module_names.push(name); - } - - // Delegate to the C++ bindings to create some data here. Once this is a - // tried-and-true interface we may wish to try to upstream some of this - // to LLVM itself, right now we reimplement a lot of what they do - // upstream... - let data = llvm::LLVMRustCreateThinLTOData( - thin_modules.as_ptr(), - thin_modules.len() as u32, - symbol_white_list.as_ptr(), - symbol_white_list.len() as u32, - ); - if data.is_null() { - let msg = format!("failed to prepare thin LTO context"); - return Err(write::llvm_err(&diag_handler, msg)) - } - let data = ThinData(data); - info!("thin LTO data created"); - timeline.record("data"); - - // Throw our data in an `Arc` as we'll be sharing it across threads. We - // also put all memory referenced by the C++ data (buffers, ids, etc) - // into the arc as well. After this we'll create a thin module - // translation per module in this data. - let shared = Arc::new(ThinShared { - data, - thin_buffers, - serialized_modules: serialized, - module_names, - }); - Ok((0..shared.module_names.len()).map(|i| { - LtoModuleTranslation::Thin(ThinModule { - shared: shared.clone(), - idx: i, - }) - }).collect()) - } -} - -fn run_pass_manager(cgcx: &CodegenContext, - tm: TargetMachineRef, - llmod: ModuleRef, - config: &ModuleConfig, - thin: bool) { - // Now we have one massive module inside of llmod. Time to run the - // LTO-specific optimization passes that LLVM provides. - // - // This code is based off the code found in llvm's LTO code generator: - // tools/lto/LTOCodeGenerator.cpp - debug!("running the pass manager"); - unsafe { - let pm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, pm, llmod); - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); - assert!(!pass.is_null()); - llvm::LLVMRustAddPass(pm, pass); - - // When optimizing for LTO we don't actually pass in `-O0`, but we force - // it to always happen at least with `-O1`. - // - // With ThinLTO we mess around a lot with symbol visibility in a way - // that will actually cause linking failures if we optimize at O0 which - // notable is lacking in dead code elimination. To ensure we at least - // get some optimizations and correctly link we forcibly switch to `-O1` - // to get dead code elimination. - // - // Note that in general this shouldn't matter too much as you typically - // only turn on ThinLTO when you're compiling with optimizations - // otherwise. - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); - let opt_level = match opt_level { - llvm::CodeGenOptLevel::None => llvm::CodeGenOptLevel::Less, - level => level, - }; - with_llvm_pmb(llmod, config, opt_level, false, &mut |b| { - if thin { - if !llvm::LLVMRustPassManagerBuilderPopulateThinLTOPassManager(b, pm) { - panic!("this version of LLVM does not support ThinLTO"); - } - } else { - llvm::LLVMPassManagerBuilderPopulateLTOPassManager(b, pm, - /* Internalize = */ False, - /* RunInliner = */ True); - } - }); - - let pass = llvm::LLVMRustFindAndCreatePass("verify\0".as_ptr() as *const _); - assert!(!pass.is_null()); - llvm::LLVMRustAddPass(pm, pass); - - time_ext(cgcx.time_passes, None, "LTO passes", || - llvm::LLVMRunPassManager(pm, llmod)); - - llvm::LLVMDisposePassManager(pm); - } - debug!("lto done"); -} - -pub enum SerializedModule { - Local(ModuleBuffer), - FromRlib(Vec), -} - -impl SerializedModule { - fn data(&self) -> &[u8] { - match *self { - SerializedModule::Local(ref m) => m.data(), - SerializedModule::FromRlib(ref m) => m, - } - } -} - -pub struct ModuleBuffer(*mut llvm::ModuleBuffer); - -unsafe impl Send for ModuleBuffer {} -unsafe impl Sync for ModuleBuffer {} - -impl ModuleBuffer { - pub fn new(m: ModuleRef) -> ModuleBuffer { - ModuleBuffer(unsafe { - llvm::LLVMRustModuleBufferCreate(m) - }) - } - - pub fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustModuleBufferPtr(self.0); - let len = llvm::LLVMRustModuleBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for ModuleBuffer { - fn drop(&mut self) { - unsafe { llvm::LLVMRustModuleBufferFree(self.0); } - } -} - -pub struct ThinModule { - shared: Arc, - idx: usize, -} - -struct ThinShared { - data: ThinData, - thin_buffers: Vec, - serialized_modules: Vec, - module_names: Vec, -} - -struct ThinData(*mut llvm::ThinLTOData); - -unsafe impl Send for ThinData {} -unsafe impl Sync for ThinData {} - -impl Drop for ThinData { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustFreeThinLTOData(self.0); - } - } -} - -pub struct ThinBuffer(*mut llvm::ThinLTOBuffer); - -unsafe impl Send for ThinBuffer {} -unsafe impl Sync for ThinBuffer {} - -impl ThinBuffer { - pub fn new(m: ModuleRef) -> ThinBuffer { - unsafe { - let buffer = llvm::LLVMRustThinLTOBufferCreate(m); - ThinBuffer(buffer) - } - } - - pub fn data(&self) -> &[u8] { - unsafe { - let ptr = llvm::LLVMRustThinLTOBufferPtr(self.0) as *const _; - let len = llvm::LLVMRustThinLTOBufferLen(self.0); - slice::from_raw_parts(ptr, len) - } - } -} - -impl Drop for ThinBuffer { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustThinLTOBufferFree(self.0); - } - } -} - -impl ThinModule { - fn name(&self) -> &str { - self.shared.module_names[self.idx].to_str().unwrap() - } - - fn cost(&self) -> u64 { - // Yes, that's correct, we're using the size of the bytecode as an - // indicator for how costly this codegen unit is. - self.data().len() as u64 - } - - fn data(&self) -> &[u8] { - let a = self.shared.thin_buffers.get(self.idx).map(|b| b.data()); - a.unwrap_or_else(|| { - let len = self.shared.thin_buffers.len(); - self.shared.serialized_modules[self.idx - len].data() - }) - } - - unsafe fn optimize(&mut self, cgcx: &CodegenContext, timeline: &mut Timeline) - -> Result - { - let diag_handler = cgcx.create_diag_handler(); - let tm = (cgcx.tm_factory)().map_err(|e| { - write::llvm_err(&diag_handler, e) - })?; - - // Right now the implementation we've got only works over serialized - // modules, so we create a fresh new LLVM context and parse the module - // into that context. One day, however, we may do this for upstream - // crates but for locally translated modules we may be able to reuse - // that LLVM Context and Module. - let llcx = llvm::LLVMRustContextCreate(cgcx.fewer_names); - let llmod = llvm::LLVMRustParseBitcodeForThinLTO( - llcx, - self.data().as_ptr(), - self.data().len(), - self.shared.module_names[self.idx].as_ptr(), - ); - if llmod.is_null() { - let msg = format!("failed to parse bitcode for thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)); - } - let mtrans = ModuleTranslation { - source: ModuleSource::Translated(ModuleLlvm { - llmod, - llcx, - tm, - }), - llmod_id: self.name().to_string(), - name: self.name().to_string(), - kind: ModuleKind::Regular, - }; - cgcx.save_temp_bitcode(&mtrans, "thin-lto-input"); - - // Before we do much else find the "main" `DICompileUnit` that we'll be - // using below. If we find more than one though then rustc has changed - // in a way we're not ready for, so generate an ICE by returning - // an error. - let mut cu1 = ptr::null_mut(); - let mut cu2 = ptr::null_mut(); - llvm::LLVMRustThinLTOGetDICompileUnit(llmod, &mut cu1, &mut cu2); - if !cu2.is_null() { - let msg = format!("multiple source DICompileUnits found"); - return Err(write::llvm_err(&diag_handler, msg)) - } - - // Like with "fat" LTO, get some better optimizations if landing pads - // are disabled by removing all landing pads. - if cgcx.no_landing_pads { - llvm::LLVMRustMarkAllFunctionsNounwind(llmod); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-nounwind"); - timeline.record("nounwind"); - } - - // Up next comes the per-module local analyses that we do for Thin LTO. - // Each of these functions is basically copied from the LLVM - // implementation and then tailored to suit this implementation. Ideally - // each of these would be supported by upstream LLVM but that's perhaps - // a patch for another day! - // - // You can find some more comments about these functions in the LLVM - // bindings we've got (currently `PassWrapper.cpp`) - if !llvm::LLVMRustPrepareThinLTORename(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rename"); - timeline.record("rename"); - if !llvm::LLVMRustPrepareThinLTOResolveWeak(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-resolve"); - timeline.record("resolve"); - if !llvm::LLVMRustPrepareThinLTOInternalize(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-internalize"); - timeline.record("internalize"); - if !llvm::LLVMRustPrepareThinLTOImport(self.shared.data.0, llmod) { - let msg = format!("failed to prepare thin LTO module"); - return Err(write::llvm_err(&diag_handler, msg)) - } - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-import"); - timeline.record("import"); - - // Ok now this is a bit unfortunate. This is also something you won't - // find upstream in LLVM's ThinLTO passes! This is a hack for now to - // work around bugs in LLVM. - // - // First discovered in #45511 it was found that as part of ThinLTO - // importing passes LLVM will import `DICompileUnit` metadata - // information across modules. This means that we'll be working with one - // LLVM module that has multiple `DICompileUnit` instances in it (a - // bunch of `llvm.dbg.cu` members). Unfortunately there's a number of - // bugs in LLVM's backend which generates invalid DWARF in a situation - // like this: - // - // https://bugs.llvm.org/show_bug.cgi?id=35212 - // https://bugs.llvm.org/show_bug.cgi?id=35562 - // - // While the first bug there is fixed the second ended up causing #46346 - // which was basically a resurgence of #45511 after LLVM's bug 35212 was - // fixed. - // - // This function below is a huge hack around this problem. The function - // below is defined in `PassWrapper.cpp` and will basically "merge" - // all `DICompileUnit` instances in a module. Basically it'll take all - // the objects, rewrite all pointers of `DISubprogram` to point to the - // first `DICompileUnit`, and then delete all the other units. - // - // This is probably mangling to the debug info slightly (but hopefully - // not too much) but for now at least gets LLVM to emit valid DWARF (or - // so it appears). Hopefully we can remove this once upstream bugs are - // fixed in LLVM. - llvm::LLVMRustThinLTOPatchDICompileUnit(llmod, cu1); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-patch"); - timeline.record("patch"); - - // Alright now that we've done everything related to the ThinLTO - // analysis it's time to run some optimizations! Here we use the same - // `run_pass_manager` as the "fat" LTO above except that we tell it to - // populate a thin-specific pass manager, which presumably LLVM treats a - // little differently. - info!("running thin lto passes over {}", mtrans.name); - let config = cgcx.config(mtrans.kind); - run_pass_manager(cgcx, tm, llmod, config, true); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-pm"); - timeline.record("thin-done"); - - // FIXME: this is a hack around a bug in LLVM right now. Discovered in - // #46910 it was found out that on 32-bit MSVC LLVM will hit a codegen - // error if there's an available_externally function in the LLVM module. - // Typically we don't actually use these functions but ThinLTO makes - // heavy use of them when inlining across modules. - // - // Tracked upstream at https://bugs.llvm.org/show_bug.cgi?id=35736 this - // function call (and its definition on the C++ side of things) - // shouldn't be necessary eventually and we can safetly delete these few - // lines. - llvm::LLVMRustThinLTORemoveAvailableExternally(llmod); - cgcx.save_temp_bitcode(&mtrans, "thin-lto-after-rm-ae"); - timeline.record("no-ae"); - - Ok(mtrans) - } -} diff --git a/src/librustc_trans/back/rpath.rs b/src/librustc_trans/back/rpath.rs deleted file mode 100644 index 8e5e7d37648..00000000000 --- a/src/librustc_trans/back/rpath.rs +++ /dev/null @@ -1,282 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashSet; -use std::env; -use std::path::{Path, PathBuf}; -use std::fs; - -use rustc::hir::def_id::CrateNum; -use rustc::middle::cstore::LibSource; - -pub struct RPathConfig<'a> { - pub used_crates: &'a [(CrateNum, LibSource)], - pub out_filename: PathBuf, - pub is_like_osx: bool, - pub has_rpath: bool, - pub linker_is_gnu: bool, - pub get_install_prefix_lib_path: &'a mut FnMut() -> PathBuf, -} - -pub fn get_rpath_flags(config: &mut RPathConfig) -> Vec { - // No rpath on windows - if !config.has_rpath { - return Vec::new(); - } - - let mut flags = Vec::new(); - - debug!("preparing the RPATH!"); - - let libs = config.used_crates.clone(); - let libs = libs.iter().filter_map(|&(_, ref l)| l.option()).collect::>(); - let rpaths = get_rpaths(config, &libs); - flags.extend_from_slice(&rpaths_to_flags(&rpaths)); - - // Use DT_RUNPATH instead of DT_RPATH if available - if config.linker_is_gnu { - flags.push("-Wl,--enable-new-dtags".to_string()); - } - - flags -} - -fn rpaths_to_flags(rpaths: &[String]) -> Vec { - let mut ret = Vec::new(); - for rpath in rpaths { - if rpath.contains(',') { - ret.push("-Wl,-rpath".into()); - ret.push("-Xlinker".into()); - ret.push(rpath.clone()); - } else { - ret.push(format!("-Wl,-rpath,{}", &(*rpath))); - } - } - return ret; -} - -fn get_rpaths(config: &mut RPathConfig, libs: &[PathBuf]) -> Vec { - debug!("output: {:?}", config.out_filename.display()); - debug!("libs:"); - for libpath in libs { - debug!(" {:?}", libpath.display()); - } - - // Use relative paths to the libraries. Binaries can be moved - // as long as they maintain the relative relationship to the - // crates they depend on. - let rel_rpaths = get_rpaths_relative_to_output(config, libs); - - // And a final backup rpath to the global library location. - let fallback_rpaths = vec![get_install_prefix_rpath(config)]; - - fn log_rpaths(desc: &str, rpaths: &[String]) { - debug!("{} rpaths:", desc); - for rpath in rpaths { - debug!(" {}", *rpath); - } - } - - log_rpaths("relative", &rel_rpaths); - log_rpaths("fallback", &fallback_rpaths); - - let mut rpaths = rel_rpaths; - rpaths.extend_from_slice(&fallback_rpaths); - - // Remove duplicates - let rpaths = minimize_rpaths(&rpaths); - return rpaths; -} - -fn get_rpaths_relative_to_output(config: &mut RPathConfig, - libs: &[PathBuf]) -> Vec { - libs.iter().map(|a| get_rpath_relative_to_output(config, a)).collect() -} - -fn get_rpath_relative_to_output(config: &mut RPathConfig, lib: &Path) -> String { - // Mac doesn't appear to support $ORIGIN - let prefix = if config.is_like_osx { - "@loader_path" - } else { - "$ORIGIN" - }; - - let cwd = env::current_dir().unwrap(); - let mut lib = fs::canonicalize(&cwd.join(lib)).unwrap_or(cwd.join(lib)); - lib.pop(); - let mut output = cwd.join(&config.out_filename); - output.pop(); - let output = fs::canonicalize(&output).unwrap_or(output); - let relative = path_relative_from(&lib, &output) - .expect(&format!("couldn't create relative path from {:?} to {:?}", output, lib)); - // FIXME (#9639): This needs to handle non-utf8 paths - format!("{}/{}", prefix, - relative.to_str().expect("non-utf8 component in path")) -} - -// This routine is adapted from the *old* Path's `path_relative_from` -// function, which works differently from the new `relative_from` function. -// In particular, this handles the case on unix where both paths are -// absolute but with only the root as the common directory. -fn path_relative_from(path: &Path, base: &Path) -> Option { - use std::path::Component; - - if path.is_absolute() != base.is_absolute() { - if path.is_absolute() { - Some(PathBuf::from(path)) - } else { - None - } - } else { - let mut ita = path.components(); - let mut itb = base.components(); - let mut comps: Vec = vec![]; - loop { - match (ita.next(), itb.next()) { - (None, None) => break, - (Some(a), None) => { - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - (None, _) => comps.push(Component::ParentDir), - (Some(a), Some(b)) if comps.is_empty() && a == b => (), - (Some(a), Some(b)) if b == Component::CurDir => comps.push(a), - (Some(_), Some(b)) if b == Component::ParentDir => return None, - (Some(a), Some(_)) => { - comps.push(Component::ParentDir); - for _ in itb { - comps.push(Component::ParentDir); - } - comps.push(a); - comps.extend(ita.by_ref()); - break; - } - } - } - Some(comps.iter().map(|c| c.as_os_str()).collect()) - } -} - - -fn get_install_prefix_rpath(config: &mut RPathConfig) -> String { - let path = (config.get_install_prefix_lib_path)(); - let path = env::current_dir().unwrap().join(&path); - // FIXME (#9639): This needs to handle non-utf8 paths - path.to_str().expect("non-utf8 component in rpath").to_string() -} - -fn minimize_rpaths(rpaths: &[String]) -> Vec { - let mut set = HashSet::new(); - let mut minimized = Vec::new(); - for rpath in rpaths { - if set.insert(rpath) { - minimized.push(rpath.clone()); - } - } - minimized -} - -#[cfg(all(unix, test))] -mod tests { - use super::{RPathConfig}; - use super::{minimize_rpaths, rpaths_to_flags, get_rpath_relative_to_output}; - use std::path::{Path, PathBuf}; - - #[test] - fn test_rpaths_to_flags() { - let flags = rpaths_to_flags(&[ - "path1".to_string(), - "path2".to_string() - ]); - assert_eq!(flags, - ["-Wl,-rpath,path1", - "-Wl,-rpath,path2"]); - } - - #[test] - fn test_minimize1() { - let res = minimize_rpaths(&[ - "rpath1".to_string(), - "rpath2".to_string(), - "rpath1".to_string() - ]); - assert!(res == [ - "rpath1", - "rpath2", - ]); - } - - #[test] - fn test_minimize2() { - let res = minimize_rpaths(&[ - "1a".to_string(), - "2".to_string(), - "2".to_string(), - "1a".to_string(), - "4a".to_string(), - "1a".to_string(), - "2".to_string(), - "3".to_string(), - "4a".to_string(), - "3".to_string() - ]); - assert!(res == [ - "1a", - "2", - "4a", - "3", - ]); - } - - #[test] - fn test_rpath_relative() { - if cfg!(target_os = "macos") { - let config = &mut RPathConfig { - used_crates: Vec::new(), - has_rpath: true, - is_like_osx: true, - linker_is_gnu: false, - out_filename: PathBuf::from("bin/rustc"), - get_install_prefix_lib_path: &mut || panic!(), - }; - let res = get_rpath_relative_to_output(config, - Path::new("lib/libstd.so")); - assert_eq!(res, "@loader_path/../lib"); - } else { - let config = &mut RPathConfig { - used_crates: Vec::new(), - out_filename: PathBuf::from("bin/rustc"), - get_install_prefix_lib_path: &mut || panic!(), - has_rpath: true, - is_like_osx: false, - linker_is_gnu: true, - }; - let res = get_rpath_relative_to_output(config, - Path::new("lib/libstd.so")); - assert_eq!(res, "$ORIGIN/../lib"); - } - } - - #[test] - fn test_xlinker() { - let args = rpaths_to_flags(&[ - "a/normal/path".to_string(), - "a,comma,path".to_string() - ]); - - assert_eq!(args, vec![ - "-Wl,-rpath,a/normal/path".to_string(), - "-Wl,-rpath".to_string(), - "-Xlinker".to_string(), - "a,comma,path".to_string() - ]); - } -} diff --git a/src/librustc_trans/back/symbol_export.rs b/src/librustc_trans/back/symbol_export.rs deleted file mode 100644 index d7785522069..00000000000 --- a/src/librustc_trans/back/symbol_export.rs +++ /dev/null @@ -1,395 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc_data_structures::sync::Lrc; -use std::sync::Arc; - -use monomorphize::Instance; -use rustc::hir; -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE, CRATE_DEF_INDEX}; -use rustc::ich::Fingerprint; -use rustc::middle::exported_symbols::{SymbolExportLevel, ExportedSymbol, metadata_symbol_name}; -use rustc::session::config; -use rustc::ty::{TyCtxt, SymbolName}; -use rustc::ty::maps::Providers; -use rustc::ty::subst::Substs; -use rustc::util::nodemap::{FxHashMap, DefIdMap}; -use rustc_allocator::ALLOCATOR_METHODS; -use rustc_data_structures::indexed_vec::IndexVec; -use std::collections::hash_map::Entry::*; - -pub type ExportedSymbols = FxHashMap< - CrateNum, - Arc>, ->; - -pub fn threshold(tcx: TyCtxt) -> SymbolExportLevel { - crates_export_threshold(&tcx.sess.crate_types.borrow()) -} - -fn crate_export_threshold(crate_type: config::CrateType) -> SymbolExportLevel { - match crate_type { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeProcMacro | - config::CrateTypeCdylib => SymbolExportLevel::C, - config::CrateTypeRlib | - config::CrateTypeDylib => SymbolExportLevel::Rust, - } -} - -pub fn crates_export_threshold(crate_types: &[config::CrateType]) - -> SymbolExportLevel { - if crate_types.iter().any(|&crate_type| { - crate_export_threshold(crate_type) == SymbolExportLevel::Rust - }) { - SymbolExportLevel::Rust - } else { - SymbolExportLevel::C - } -} - -fn reachable_non_generics_provider<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_trans() { - return Lrc::new(DefIdMap()) - } - - // Check to see if this crate is a "special runtime crate". These - // crates, implementation details of the standard library, typically - // have a bunch of `pub extern` and `#[no_mangle]` functions as the - // ABI between them. We don't want their symbols to have a `C` - // export level, however, as they're just implementation details. - // Down below we'll hardwire all of the symbols to the `Rust` export - // level instead. - let special_runtime_crate = tcx.is_panic_runtime(LOCAL_CRATE) || - tcx.is_compiler_builtins(LOCAL_CRATE); - - let mut reachable_non_generics: DefIdMap<_> = tcx.reachable_set(LOCAL_CRATE).0 - .iter() - .filter_map(|&node_id| { - // We want to ignore some FFI functions that are not exposed from - // this crate. Reachable FFI functions can be lumped into two - // categories: - // - // 1. Those that are included statically via a static library - // 2. Those included otherwise (e.g. dynamically or via a framework) - // - // Although our LLVM module is not literally emitting code for the - // statically included symbols, it's an export of our library which - // needs to be passed on to the linker and encoded in the metadata. - // - // As a result, if this id is an FFI item (foreign item) then we only - // let it through if it's included statically. - match tcx.hir.get(node_id) { - hir::map::NodeForeignItem(..) => { - let def_id = tcx.hir.local_def_id(node_id); - if tcx.is_statically_included_foreign_item(def_id) { - Some(def_id) - } else { - None - } - } - - // Only consider nodes that actually have exported symbols. - hir::map::NodeItem(&hir::Item { - node: hir::ItemStatic(..), - .. - }) | - hir::map::NodeItem(&hir::Item { - node: hir::ItemFn(..), .. - }) | - hir::map::NodeImplItem(&hir::ImplItem { - node: hir::ImplItemKind::Method(..), - .. - }) => { - let def_id = tcx.hir.local_def_id(node_id); - let generics = tcx.generics_of(def_id); - if !generics.requires_monomorphization(tcx) && - // Functions marked with #[inline] are only ever translated - // with "internal" linkage and are never exported. - !Instance::mono(tcx, def_id).def.requires_local(tcx) { - Some(def_id) - } else { - None - } - } - - _ => None - } - }) - .map(|def_id| { - let export_level = if special_runtime_crate { - let name = tcx.symbol_name(Instance::mono(tcx, def_id)).as_str(); - // We can probably do better here by just ensuring that - // it has hidden visibility rather than public - // visibility, as this is primarily here to ensure it's - // not stripped during LTO. - // - // In general though we won't link right if these - // symbols are stripped, and LTO currently strips them. - if &*name == "rust_eh_personality" || - &*name == "rust_eh_register_frames" || - &*name == "rust_eh_unregister_frames" { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } - } else { - symbol_export_level(tcx, def_id) - }; - debug!("EXPORTED SYMBOL (local): {} ({:?})", - tcx.symbol_name(Instance::mono(tcx, def_id)), - export_level); - (def_id, export_level) - }) - .collect(); - - if let Some(id) = *tcx.sess.derive_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - if let Some(id) = *tcx.sess.plugin_registrar_fn.get() { - let def_id = tcx.hir.local_def_id(id); - reachable_non_generics.insert(def_id, SymbolExportLevel::C); - } - - Lrc::new(reachable_non_generics) -} - -fn is_reachable_non_generic_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - let export_threshold = threshold(tcx); - - if let Some(&level) = tcx.reachable_non_generics(def_id.krate).get(&def_id) { - level.is_below_threshold(export_threshold) - } else { - false - } -} - -fn is_reachable_non_generic_provider_extern<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> bool { - tcx.reachable_non_generics(def_id.krate).contains_key(&def_id) -} - -fn exported_symbols_provider_local<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Arc, - SymbolExportLevel)>> -{ - assert_eq!(cnum, LOCAL_CRATE); - - if !tcx.sess.opts.output_types.should_trans() { - return Arc::new(vec![]) - } - - let mut symbols: Vec<_> = tcx.reachable_non_generics(LOCAL_CRATE) - .iter() - .map(|(&def_id, &level)| { - (ExportedSymbol::NonGeneric(def_id), level) - }) - .collect(); - - if let Some(_) = *tcx.sess.entry_fn.borrow() { - let symbol_name = "main".to_string(); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - - if tcx.sess.allocator_kind.get().is_some() { - for method in ALLOCATOR_METHODS { - let symbol_name = format!("__rust_{}", method.name); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - } - - if tcx.sess.opts.debugging_opts.pgo_gen.is_some() { - // These are weak symbols that point to the profile version and the - // profile name, which need to be treated as exported so LTO doesn't nix - // them. - const PROFILER_WEAK_SYMBOLS: [&'static str; 2] = [ - "__llvm_profile_raw_version", - "__llvm_profile_filename", - ]; - for sym in &PROFILER_WEAK_SYMBOLS { - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(sym)); - symbols.push((exported_symbol, SymbolExportLevel::C)); - } - } - - if tcx.sess.crate_types.borrow().contains(&config::CrateTypeDylib) { - let symbol_name = metadata_symbol_name(tcx); - let exported_symbol = ExportedSymbol::NoDefId(SymbolName::new(&symbol_name)); - - symbols.push((exported_symbol, SymbolExportLevel::Rust)); - } - - if tcx.share_generics() && tcx.local_crate_exports_generics() { - use rustc::mir::mono::{Linkage, Visibility, MonoItem}; - use rustc::ty::InstanceDef; - - // Normally, we require that shared monomorphizations are not hidden, - // because if we want to re-use a monomorphization from a Rust dylib, it - // needs to be exported. - // However, on platforms that don't allow for Rust dylibs, having - // external linkage is enough for monomorphization to be linked to. - let need_visibility = tcx.sess.target.target.options.dynamic_linking && - !tcx.sess.target.target.options.only_cdylib; - - let (_, cgus) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); - - for (mono_item, &(linkage, visibility)) in cgus.iter() - .flat_map(|cgu| cgu.items().iter()) { - if linkage != Linkage::External { - // We can only re-use things with external linkage, otherwise - // we'll get a linker error - continue - } - - if need_visibility && visibility == Visibility::Hidden { - // If we potentially share things from Rust dylibs, they must - // not be hidden - continue - } - - if let &MonoItem::Fn(Instance { - def: InstanceDef::Item(def_id), - substs, - }) = mono_item { - if substs.types().next().is_some() { - symbols.push((ExportedSymbol::Generic(def_id, substs), - SymbolExportLevel::Rust)); - } - } - } - } - - // Sort so we get a stable incr. comp. hash. - symbols.sort_unstable_by(|&(ref symbol1, ..), &(ref symbol2, ..)| { - symbol1.compare_stable(tcx, symbol2) - }); - - Arc::new(symbols) -} - -fn upstream_monomorphizations_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum) - -> Lrc, CrateNum>>>> -{ - debug_assert!(cnum == LOCAL_CRATE); - - let cnums = tcx.all_crate_nums(LOCAL_CRATE); - - let mut instances = DefIdMap(); - - let cnum_stable_ids: IndexVec = { - let mut cnum_stable_ids = IndexVec::from_elem_n(Fingerprint::ZERO, - cnums.len() + 1); - - for &cnum in cnums.iter() { - cnum_stable_ids[cnum] = tcx.def_path_hash(DefId { - krate: cnum, - index: CRATE_DEF_INDEX, - }).0; - } - - cnum_stable_ids - }; - - for &cnum in cnums.iter() { - for &(ref exported_symbol, _) in tcx.exported_symbols(cnum).iter() { - if let &ExportedSymbol::Generic(def_id, substs) = exported_symbol { - let substs_map = instances.entry(def_id) - .or_insert_with(|| FxHashMap()); - - match substs_map.entry(substs) { - Occupied(mut e) => { - // If there are multiple monomorphizations available, - // we select one deterministically. - let other_cnum = *e.get(); - if cnum_stable_ids[other_cnum] > cnum_stable_ids[cnum] { - e.insert(cnum); - } - } - Vacant(e) => { - e.insert(cnum); - } - } - } - } - } - - Lrc::new(instances.into_iter() - .map(|(key, value)| (key, Lrc::new(value))) - .collect()) -} - -fn upstream_monomorphizations_for_provider<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - def_id: DefId) - -> Option, CrateNum>>> -{ - debug_assert!(!def_id.is_local()); - tcx.upstream_monomorphizations(LOCAL_CRATE) - .get(&def_id) - .cloned() -} - -fn is_unreachable_local_definition_provider(tcx: TyCtxt, def_id: DefId) -> bool { - if let Some(node_id) = tcx.hir.as_local_node_id(def_id) { - !tcx.reachable_set(LOCAL_CRATE).0.contains(&node_id) - } else { - bug!("is_unreachable_local_definition called with non-local DefId: {:?}", - def_id) - } -} - -pub fn provide(providers: &mut Providers) { - providers.reachable_non_generics = reachable_non_generics_provider; - providers.is_reachable_non_generic = is_reachable_non_generic_provider_local; - providers.exported_symbols = exported_symbols_provider_local; - providers.upstream_monomorphizations = upstream_monomorphizations_provider; - providers.is_unreachable_local_definition = is_unreachable_local_definition_provider; -} - -pub fn provide_extern(providers: &mut Providers) { - providers.is_reachable_non_generic = is_reachable_non_generic_provider_extern; - providers.upstream_monomorphizations_for = upstream_monomorphizations_for_provider; -} - -fn symbol_export_level(tcx: TyCtxt, sym_def_id: DefId) -> SymbolExportLevel { - // We export anything that's not mangled at the "C" layer as it probably has - // to do with ABI concerns. We do not, however, apply such treatment to - // special symbols in the standard library for various plumbing between - // core/std/allocators/etc. For example symbols used to hook up allocation - // are not considered for export - let trans_fn_attrs = tcx.trans_fn_attrs(sym_def_id); - let is_extern = trans_fn_attrs.contains_extern_indicator(); - let std_internal = trans_fn_attrs.flags.contains(TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL); - - if is_extern && !std_internal { - SymbolExportLevel::C - } else { - SymbolExportLevel::Rust - } -} diff --git a/src/librustc_trans/back/wasm.rs b/src/librustc_trans/back/wasm.rs deleted file mode 100644 index d6d386c9fbe..00000000000 --- a/src/librustc_trans/back/wasm.rs +++ /dev/null @@ -1,261 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::BTreeMap; -use std::fs; -use std::path::Path; -use std::str; - -use rustc_data_structures::fx::FxHashMap; -use serialize::leb128; - -// https://webassembly.github.io/spec/core/binary/modules.html#binary-importsec -const WASM_IMPORT_SECTION_ID: u8 = 2; - -const WASM_EXTERNAL_KIND_FUNCTION: u8 = 0; -const WASM_EXTERNAL_KIND_TABLE: u8 = 1; -const WASM_EXTERNAL_KIND_MEMORY: u8 = 2; -const WASM_EXTERNAL_KIND_GLOBAL: u8 = 3; - -/// Append all the custom sections listed in `sections` to the wasm binary -/// specified at `path`. -/// -/// LLVM 6 which we're using right now doesn't have the ability to create custom -/// sections in wasm files nor does LLD have the ability to merge these sections -/// into one larger section when linking. It's expected that this will -/// eventually get implemented, however! -/// -/// Until that time though this is a custom implementation in rustc to append -/// all sections to a wasm file to the finished product that LLD produces. -/// -/// Support for this is landing in LLVM in https://reviews.llvm.org/D43097, -/// although after that support will need to be in LLD as well. -pub fn add_custom_sections(path: &Path, sections: &BTreeMap>) { - if sections.len() == 0 { - return - } - - let wasm = fs::read(path).expect("failed to read wasm output"); - - // see https://webassembly.github.io/spec/core/binary/modules.html#custom-section - let mut wasm = WasmEncoder { data: wasm }; - for (section, bytes) in sections { - // write the `id` identifier, 0 for a custom section - wasm.byte(0); - - // figure out how long our name descriptor will be - let mut name = WasmEncoder::new(); - name.str(section); - - // write the length of the payload followed by all its contents - wasm.u32((bytes.len() + name.data.len()) as u32); - wasm.data.extend_from_slice(&name.data); - wasm.data.extend_from_slice(bytes); - } - - fs::write(path, &wasm.data).expect("failed to write wasm output"); -} - -/// Rewrite the module imports are listed from in a wasm module given the field -/// name to module name mapping in `import_map`. -/// -/// LLVM 6 which we're using right now doesn't have the ability to configure the -/// module a wasm symbol is import from. Rather all imported symbols come from -/// the bland `"env"` module unconditionally. Furthermore we'd *also* need -/// support in LLD for preserving these import modules, which it unfortunately -/// currently does not. -/// -/// This function is intended as a hack for now where we manually rewrite the -/// wasm output by LLVM to have the correct import modules listed. The -/// `#[wasm_import_module]` attribute in Rust translates to the module that each -/// symbol is imported from, so here we manually go through the wasm file, -/// decode it, rewrite imports, and then rewrite the wasm module. -/// -/// Support for this was added to LLVM in -/// https://github.com/llvm-mirror/llvm/commit/0f32e1365, although support still -/// needs to be added (AFAIK at the time of this writing) to LLD -pub fn rewrite_imports(path: &Path, import_map: &FxHashMap) { - if import_map.len() == 0 { - return - } - - let wasm = fs::read(path).expect("failed to read wasm output"); - let mut ret = WasmEncoder::new(); - ret.data.extend(&wasm[..8]); - - // skip the 8 byte wasm/version header - for (id, raw) in WasmSections(WasmDecoder::new(&wasm[8..])) { - ret.byte(id); - if id == WASM_IMPORT_SECTION_ID { - info!("rewriting import section"); - let data = rewrite_import_section( - &mut WasmDecoder::new(raw), - import_map, - ); - ret.bytes(&data); - } else { - info!("carry forward section {}, {} bytes long", id, raw.len()); - ret.bytes(raw); - } - } - - fs::write(path, &ret.data).expect("failed to write wasm output"); - - fn rewrite_import_section( - wasm: &mut WasmDecoder, - import_map: &FxHashMap, - ) - -> Vec - { - let mut dst = WasmEncoder::new(); - let n = wasm.u32(); - dst.u32(n); - info!("rewriting {} imports", n); - for _ in 0..n { - rewrite_import_entry(wasm, &mut dst, import_map); - } - return dst.data - } - - fn rewrite_import_entry(wasm: &mut WasmDecoder, - dst: &mut WasmEncoder, - import_map: &FxHashMap) { - // More info about the binary format here is available at: - // https://webassembly.github.io/spec/core/binary/modules.html#import-section - // - // Note that you can also find the whole point of existence of this - // function here, where we map the `module` name to a different one if - // we've got one listed. - let module = wasm.str(); - let field = wasm.str(); - let new_module = if module == "env" { - import_map.get(field).map(|s| &**s).unwrap_or(module) - } else { - module - }; - info!("import rewrite ({} => {}) / {}", module, new_module, field); - dst.str(new_module); - dst.str(field); - let kind = wasm.byte(); - dst.byte(kind); - match kind { - WASM_EXTERNAL_KIND_FUNCTION => dst.u32(wasm.u32()), - WASM_EXTERNAL_KIND_TABLE => { - dst.byte(wasm.byte()); // element_type - dst.limits(wasm.limits()); - } - WASM_EXTERNAL_KIND_MEMORY => dst.limits(wasm.limits()), - WASM_EXTERNAL_KIND_GLOBAL => { - dst.byte(wasm.byte()); // content_type - dst.bool(wasm.bool()); // mutable - } - b => panic!("unknown kind: {}", b), - } - } -} - -struct WasmSections<'a>(WasmDecoder<'a>); - -impl<'a> Iterator for WasmSections<'a> { - type Item = (u8, &'a [u8]); - - fn next(&mut self) -> Option<(u8, &'a [u8])> { - if self.0.data.len() == 0 { - return None - } - - // see https://webassembly.github.io/spec/core/binary/modules.html#sections - let id = self.0.byte(); - let section_len = self.0.u32(); - info!("new section {} / {} bytes", id, section_len); - let section = self.0.skip(section_len as usize); - Some((id, section)) - } -} - -struct WasmDecoder<'a> { - data: &'a [u8], -} - -impl<'a> WasmDecoder<'a> { - fn new(data: &'a [u8]) -> WasmDecoder<'a> { - WasmDecoder { data } - } - - fn byte(&mut self) -> u8 { - self.skip(1)[0] - } - - fn u32(&mut self) -> u32 { - let (n, l1) = leb128::read_u32_leb128(self.data); - self.data = &self.data[l1..]; - return n - } - - fn skip(&mut self, amt: usize) -> &'a [u8] { - let (data, rest) = self.data.split_at(amt); - self.data = rest; - data - } - - fn str(&mut self) -> &'a str { - let len = self.u32(); - str::from_utf8(self.skip(len as usize)).unwrap() - } - - fn bool(&mut self) -> bool { - self.byte() == 1 - } - - fn limits(&mut self) -> (u32, Option) { - let has_max = self.bool(); - (self.u32(), if has_max { Some(self.u32()) } else { None }) - } -} - -struct WasmEncoder { - data: Vec, -} - -impl WasmEncoder { - fn new() -> WasmEncoder { - WasmEncoder { data: Vec::new() } - } - - fn u32(&mut self, val: u32) { - let at = self.data.len(); - leb128::write_u32_leb128(&mut self.data, at, val); - } - - fn byte(&mut self, val: u8) { - self.data.push(val); - } - - fn bytes(&mut self, val: &[u8]) { - self.u32(val.len() as u32); - self.data.extend_from_slice(val); - } - - fn str(&mut self, val: &str) { - self.bytes(val.as_bytes()) - } - - fn bool(&mut self, b: bool) { - self.byte(b as u8); - } - - fn limits(&mut self, limits: (u32, Option)) { - self.bool(limits.1.is_some()); - self.u32(limits.0); - if let Some(c) = limits.1 { - self.u32(c); - } - } -} diff --git a/src/librustc_trans/back/write.rs b/src/librustc_trans/back/write.rs deleted file mode 100644 index acfe7c33028..00000000000 --- a/src/librustc_trans/back/write.rs +++ /dev/null @@ -1,2392 +0,0 @@ -// Copyright 2013-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use attributes; -use back::bytecode::{self, RLIB_BYTECODE_EXTENSION}; -use back::lto::{self, ModuleBuffer, ThinBuffer}; -use back::link::{self, get_linker, remove}; -use back::command::Command; -use back::linker::LinkerInfo; -use back::symbol_export::ExportedSymbols; -use base; -use consts; -use rustc_incremental::{copy_cgu_workproducts_to_incr_comp_cache_dir, in_incr_comp_dir}; -use rustc::dep_graph::{WorkProduct, WorkProductId, WorkProductFileKind}; -use rustc::middle::cstore::{LinkMeta, EncodedMetadata}; -use rustc::session::config::{self, OutputFilenames, OutputType, Passes, SomePasses, - AllPasses, Sanitizer, Lto}; -use rustc::session::Session; -use rustc::util::nodemap::FxHashMap; -use time_graph::{self, TimeGraph, Timeline}; -use llvm; -use llvm::{ModuleRef, TargetMachineRef, PassManagerRef, DiagnosticInfoRef}; -use llvm::{SMDiagnosticRef, ContextRef}; -use {CrateTranslation, ModuleSource, ModuleTranslation, CompiledModule, ModuleKind}; -use CrateInfo; -use rustc::hir::def_id::{CrateNum, LOCAL_CRATE}; -use rustc::ty::TyCtxt; -use rustc::util::common::{time_ext, time_depth, set_time_depth, print_time_passes_entry}; -use rustc::util::common::path2cstr; -use rustc::util::fs::{link_or_copy}; -use errors::{self, Handler, Level, DiagnosticBuilder, FatalError, DiagnosticId}; -use errors::emitter::{Emitter}; -use syntax::attr; -use syntax::ext::hygiene::Mark; -use syntax_pos::MultiSpan; -use syntax_pos::symbol::Symbol; -use type_::Type; -use context::{is_pie_binary, get_reloc_model}; -use common::{C_bytes_in_context, val_ty}; -use jobserver::{Client, Acquired}; -use rustc_demangle; - -use std::any::Any; -use std::ffi::{CString, CStr}; -use std::fs; -use std::io::{self, Write}; -use std::mem; -use std::path::{Path, PathBuf}; -use std::str; -use std::sync::Arc; -use std::sync::mpsc::{channel, Sender, Receiver}; -use std::slice; -use std::time::Instant; -use std::thread; -use libc::{c_uint, c_void, c_char, size_t}; - -pub const RELOC_MODEL_ARGS : [(&'static str, llvm::RelocMode); 7] = [ - ("pic", llvm::RelocMode::PIC), - ("static", llvm::RelocMode::Static), - ("default", llvm::RelocMode::Default), - ("dynamic-no-pic", llvm::RelocMode::DynamicNoPic), - ("ropi", llvm::RelocMode::ROPI), - ("rwpi", llvm::RelocMode::RWPI), - ("ropi-rwpi", llvm::RelocMode::ROPI_RWPI), -]; - -pub const CODE_GEN_MODEL_ARGS: &[(&str, llvm::CodeModel)] = &[ - ("small", llvm::CodeModel::Small), - ("kernel", llvm::CodeModel::Kernel), - ("medium", llvm::CodeModel::Medium), - ("large", llvm::CodeModel::Large), -]; - -pub const TLS_MODEL_ARGS : [(&'static str, llvm::ThreadLocalMode); 4] = [ - ("global-dynamic", llvm::ThreadLocalMode::GeneralDynamic), - ("local-dynamic", llvm::ThreadLocalMode::LocalDynamic), - ("initial-exec", llvm::ThreadLocalMode::InitialExec), - ("local-exec", llvm::ThreadLocalMode::LocalExec), -]; - -pub fn llvm_err(handler: &errors::Handler, msg: String) -> FatalError { - match llvm::last_error() { - Some(err) => handler.fatal(&format!("{}: {}", msg, err)), - None => handler.fatal(&msg), - } -} - -pub fn write_output_file( - handler: &errors::Handler, - target: llvm::TargetMachineRef, - pm: llvm::PassManagerRef, - m: ModuleRef, - output: &Path, - file_type: llvm::FileType) -> Result<(), FatalError> { - unsafe { - let output_c = path2cstr(output); - let result = llvm::LLVMRustWriteOutputFile( - target, pm, m, output_c.as_ptr(), file_type); - if result.into_result().is_err() { - let msg = format!("could not write output to {}", output.display()); - Err(llvm_err(handler, msg)) - } else { - Ok(()) - } - } -} - -fn get_llvm_opt_level(optimize: config::OptLevel) -> llvm::CodeGenOptLevel { - match optimize { - config::OptLevel::No => llvm::CodeGenOptLevel::None, - config::OptLevel::Less => llvm::CodeGenOptLevel::Less, - config::OptLevel::Default => llvm::CodeGenOptLevel::Default, - config::OptLevel::Aggressive => llvm::CodeGenOptLevel::Aggressive, - _ => llvm::CodeGenOptLevel::Default, - } -} - -fn get_llvm_opt_size(optimize: config::OptLevel) -> llvm::CodeGenOptSize { - match optimize { - config::OptLevel::Size => llvm::CodeGenOptSizeDefault, - config::OptLevel::SizeMin => llvm::CodeGenOptSizeAggressive, - _ => llvm::CodeGenOptSizeNone, - } -} - -pub fn create_target_machine(sess: &Session, find_features: bool) -> TargetMachineRef { - target_machine_factory(sess, find_features)().unwrap_or_else(|err| { - llvm_err(sess.diagnostic(), err).raise() - }) -} - -// If find_features is true this won't access `sess.crate_types` by assuming -// that `is_pie_binary` is false. When we discover LLVM target features -// `sess.crate_types` is uninitialized so we cannot access it. -pub fn target_machine_factory(sess: &Session, find_features: bool) - -> Arc Result + Send + Sync> -{ - let reloc_model = get_reloc_model(sess); - - let opt_level = get_llvm_opt_level(sess.opts.optimize); - let use_softfp = sess.opts.cg.soft_float; - - let ffunction_sections = sess.target.target.options.function_sections; - let fdata_sections = ffunction_sections; - - let code_model_arg = sess.opts.cg.code_model.as_ref().or( - sess.target.target.options.code_model.as_ref(), - ); - - let code_model = match code_model_arg { - Some(s) => { - match CODE_GEN_MODEL_ARGS.iter().find(|arg| arg.0 == s) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid code model", - code_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } - } - None => llvm::CodeModel::None, - }; - - let singlethread = sess.target.target.options.singlethread; - - let triple = &sess.target.target.llvm_target; - - let triple = CString::new(triple.as_bytes()).unwrap(); - let cpu = sess.target_cpu(); - let cpu = CString::new(cpu.as_bytes()).unwrap(); - let features = attributes::llvm_target_features(sess) - .collect::>() - .join(","); - let features = CString::new(features).unwrap(); - let is_pie_binary = !find_features && is_pie_binary(sess); - let trap_unreachable = sess.target.target.options.trap_unreachable; - - Arc::new(move || { - let tm = unsafe { - llvm::LLVMRustCreateTargetMachine( - triple.as_ptr(), cpu.as_ptr(), features.as_ptr(), - code_model, - reloc_model, - opt_level, - use_softfp, - is_pie_binary, - ffunction_sections, - fdata_sections, - trap_unreachable, - singlethread, - ) - }; - - if tm.is_null() { - Err(format!("Could not create LLVM TargetMachine for triple: {}", - triple.to_str().unwrap())) - } else { - Ok(tm) - } - }) -} - -/// Module-specific configuration for `optimize_and_codegen`. -pub struct ModuleConfig { - /// Names of additional optimization passes to run. - passes: Vec, - /// Some(level) to optimize at a certain level, or None to run - /// absolutely no optimizations (used for the metadata module). - pub opt_level: Option, - - /// Some(level) to optimize binary size, or None to not affect program size. - opt_size: Option, - - pgo_gen: Option, - pgo_use: String, - - // Flags indicating which outputs to produce. - emit_no_opt_bc: bool, - emit_bc: bool, - emit_bc_compressed: bool, - emit_lto_bc: bool, - emit_ir: bool, - emit_asm: bool, - emit_obj: bool, - // Miscellaneous flags. These are mostly copied from command-line - // options. - no_verify: bool, - no_prepopulate_passes: bool, - no_builtins: bool, - time_passes: bool, - vectorize_loop: bool, - vectorize_slp: bool, - merge_functions: bool, - inline_threshold: Option, - // Instead of creating an object file by doing LLVM codegen, just - // make the object file bitcode. Provides easy compatibility with - // emscripten's ecc compiler, when used as the linker. - obj_is_bitcode: bool, - no_integrated_as: bool, - embed_bitcode: bool, - embed_bitcode_marker: bool, -} - -impl ModuleConfig { - fn new(passes: Vec) -> ModuleConfig { - ModuleConfig { - passes, - opt_level: None, - opt_size: None, - - pgo_gen: None, - pgo_use: String::new(), - - emit_no_opt_bc: false, - emit_bc: false, - emit_bc_compressed: false, - emit_lto_bc: false, - emit_ir: false, - emit_asm: false, - emit_obj: false, - obj_is_bitcode: false, - embed_bitcode: false, - embed_bitcode_marker: false, - no_integrated_as: false, - - no_verify: false, - no_prepopulate_passes: false, - no_builtins: false, - time_passes: false, - vectorize_loop: false, - vectorize_slp: false, - merge_functions: false, - inline_threshold: None - } - } - - fn set_flags(&mut self, sess: &Session, no_builtins: bool) { - self.no_verify = sess.no_verify(); - self.no_prepopulate_passes = sess.opts.cg.no_prepopulate_passes; - self.no_builtins = no_builtins || sess.target.target.options.no_builtins; - self.time_passes = sess.time_passes(); - self.inline_threshold = sess.opts.cg.inline_threshold; - self.obj_is_bitcode = sess.target.target.options.obj_is_bitcode; - let embed_bitcode = sess.target.target.options.embed_bitcode || - sess.opts.debugging_opts.embed_bitcode || - sess.opts.debugging_opts.cross_lang_lto.embed_bitcode(); - if embed_bitcode { - match sess.opts.optimize { - config::OptLevel::No | - config::OptLevel::Less => { - self.embed_bitcode_marker = embed_bitcode; - } - _ => self.embed_bitcode = embed_bitcode, - } - } - - // Copy what clang does by turning on loop vectorization at O2 and - // slp vectorization at O3. Otherwise configure other optimization aspects - // of this pass manager builder. - // Turn off vectorization for emscripten, as it's not very well supported. - self.vectorize_loop = !sess.opts.cg.no_vectorize_loops && - (sess.opts.optimize == config::OptLevel::Default || - sess.opts.optimize == config::OptLevel::Aggressive) && - !sess.target.target.options.is_like_emscripten; - - self.vectorize_slp = !sess.opts.cg.no_vectorize_slp && - sess.opts.optimize == config::OptLevel::Aggressive && - !sess.target.target.options.is_like_emscripten; - - self.merge_functions = sess.opts.optimize == config::OptLevel::Default || - sess.opts.optimize == config::OptLevel::Aggressive; - } -} - -/// Assembler name and command used by codegen when no_integrated_as is enabled -struct AssemblerCommand { - name: PathBuf, - cmd: Command, -} - -/// Additional resources used by optimize_and_codegen (not module specific) -#[derive(Clone)] -pub struct CodegenContext { - // Resouces needed when running LTO - pub time_passes: bool, - pub lto: Lto, - pub no_landing_pads: bool, - pub save_temps: bool, - pub fewer_names: bool, - pub exported_symbols: Option>, - pub opts: Arc, - pub crate_types: Vec, - pub each_linked_rlib_for_lto: Vec<(CrateNum, PathBuf)>, - output_filenames: Arc, - regular_module_config: Arc, - metadata_module_config: Arc, - allocator_module_config: Arc, - pub tm_factory: Arc Result + Send + Sync>, - pub msvc_imps_needed: bool, - pub target_pointer_width: String, - debuginfo: config::DebugInfoLevel, - - // Number of cgus excluding the allocator/metadata modules - pub total_cgus: usize, - // Handler to use for diagnostics produced during codegen. - pub diag_emitter: SharedEmitter, - // LLVM passes added by plugins. - pub plugin_passes: Vec, - // LLVM optimizations for which we want to print remarks. - pub remark: Passes, - // Worker thread number - pub worker: usize, - // The incremental compilation session directory, or None if we are not - // compiling incrementally - pub incr_comp_session_dir: Option, - // Channel back to the main control thread to send messages to - coordinator_send: Sender>, - // A reference to the TimeGraph so we can register timings. None means that - // measuring is disabled. - time_graph: Option, - // The assembler command if no_integrated_as option is enabled, None otherwise - assembler_cmd: Option>, -} - -impl CodegenContext { - pub fn create_diag_handler(&self) -> Handler { - Handler::with_emitter(true, false, Box::new(self.diag_emitter.clone())) - } - - pub(crate) fn config(&self, kind: ModuleKind) -> &ModuleConfig { - match kind { - ModuleKind::Regular => &self.regular_module_config, - ModuleKind::Metadata => &self.metadata_module_config, - ModuleKind::Allocator => &self.allocator_module_config, - } - } - - pub(crate) fn save_temp_bitcode(&self, trans: &ModuleTranslation, name: &str) { - if !self.save_temps { - return - } - unsafe { - let ext = format!("{}.bc", name); - let cgu = Some(&trans.name[..]); - let path = self.output_filenames.temp_path_ext(&ext, cgu); - let cstr = path2cstr(&path); - let llmod = trans.llvm().unwrap().llmod; - llvm::LLVMWriteBitcodeToFile(llmod, cstr.as_ptr()); - } - } -} - -struct DiagnosticHandlers<'a> { - inner: Box<(&'a CodegenContext, &'a Handler)>, - llcx: ContextRef, -} - -impl<'a> DiagnosticHandlers<'a> { - fn new(cgcx: &'a CodegenContext, - handler: &'a Handler, - llcx: ContextRef) -> DiagnosticHandlers<'a> { - let data = Box::new((cgcx, handler)); - unsafe { - let arg = &*data as &(_, _) as *const _ as *mut _; - llvm::LLVMRustSetInlineAsmDiagnosticHandler(llcx, inline_asm_handler, arg); - llvm::LLVMContextSetDiagnosticHandler(llcx, diagnostic_handler, arg); - } - DiagnosticHandlers { - inner: data, - llcx: llcx, - } - } -} - -impl<'a> Drop for DiagnosticHandlers<'a> { - fn drop(&mut self) { - unsafe { - llvm::LLVMRustSetInlineAsmDiagnosticHandler(self.llcx, inline_asm_handler, 0 as *mut _); - llvm::LLVMContextSetDiagnosticHandler(self.llcx, diagnostic_handler, 0 as *mut _); - } - } -} - -unsafe extern "C" fn report_inline_asm<'a, 'b>(cgcx: &'a CodegenContext, - msg: &'b str, - cookie: c_uint) { - cgcx.diag_emitter.inline_asm_error(cookie as u32, msg.to_string()); -} - -unsafe extern "C" fn inline_asm_handler(diag: SMDiagnosticRef, - user: *const c_void, - cookie: c_uint) { - if user.is_null() { - return - } - let (cgcx, _) = *(user as *const (&CodegenContext, &Handler)); - - let msg = llvm::build_string(|s| llvm::LLVMRustWriteSMDiagnosticToString(diag, s)) - .expect("non-UTF8 SMDiagnostic"); - - report_inline_asm(cgcx, &msg, cookie); -} - -unsafe extern "C" fn diagnostic_handler(info: DiagnosticInfoRef, user: *mut c_void) { - if user.is_null() { - return - } - let (cgcx, diag_handler) = *(user as *const (&CodegenContext, &Handler)); - - match llvm::diagnostic::Diagnostic::unpack(info) { - llvm::diagnostic::InlineAsm(inline) => { - report_inline_asm(cgcx, - &llvm::twine_to_string(inline.message), - inline.cookie); - } - - llvm::diagnostic::Optimization(opt) => { - let enabled = match cgcx.remark { - AllPasses => true, - SomePasses(ref v) => v.iter().any(|s| *s == opt.pass_name), - }; - - if enabled { - diag_handler.note_without_error(&format!("optimization {} for {} at {}:{}:{}: {}", - opt.kind.describe(), - opt.pass_name, - opt.filename, - opt.line, - opt.column, - opt.message)); - } - } - llvm::diagnostic::PGO(diagnostic_ref) => { - let msg = llvm::build_string(|s| { - llvm::LLVMRustWriteDiagnosticInfoToString(diagnostic_ref, s) - }).expect("non-UTF8 PGO diagnostic"); - diag_handler.warn(&msg); - } - llvm::diagnostic::UnknownDiagnostic(..) => {}, - } -} - -// Unsafe due to LLVM calls. -unsafe fn optimize(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: &ModuleTranslation, - config: &ModuleConfig, - timeline: &mut Timeline) - -> Result<(), FatalError> -{ - let (llmod, llcx, tm) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), - ModuleSource::Preexisting(_) => { - bug!("optimize_and_codegen: called with ModuleSource::Preexisting") - } - }; - - let _handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); - - let module_name = mtrans.name.clone(); - let module_name = Some(&module_name[..]); - - if config.emit_no_opt_bc { - let out = cgcx.output_filenames.temp_path_ext("no-opt.bc", module_name); - let out = path2cstr(&out); - llvm::LLVMWriteBitcodeToFile(llmod, out.as_ptr()); - } - - if config.opt_level.is_some() { - // Create the two optimizing pass managers. These mirror what clang - // does, and are by populated by LLVM's default PassManagerBuilder. - // Each manager has a different set of passes, but they also share - // some common passes. - let fpm = llvm::LLVMCreateFunctionPassManagerForModule(llmod); - let mpm = llvm::LLVMCreatePassManager(); - - // If we're verifying or linting, add them to the function pass - // manager. - let addpass = |pass_name: &str| { - let pass_name = CString::new(pass_name).unwrap(); - let pass = llvm::LLVMRustFindAndCreatePass(pass_name.as_ptr()); - if pass.is_null() { - return false; - } - let pass_manager = match llvm::LLVMRustPassKind(pass) { - llvm::PassKind::Function => fpm, - llvm::PassKind::Module => mpm, - llvm::PassKind::Other => { - diag_handler.err("Encountered LLVM pass kind we can't handle"); - return true - }, - }; - llvm::LLVMRustAddPass(pass_manager, pass); - true - }; - - if !config.no_verify { assert!(addpass("verify")); } - if !config.no_prepopulate_passes { - llvm::LLVMRustAddAnalysisPasses(tm, fpm, llmod); - llvm::LLVMRustAddAnalysisPasses(tm, mpm, llmod); - let opt_level = config.opt_level.unwrap_or(llvm::CodeGenOptLevel::None); - let prepare_for_thin_lto = cgcx.lto == Lto::Thin || cgcx.lto == Lto::ThinLocal; - with_llvm_pmb(llmod, &config, opt_level, prepare_for_thin_lto, &mut |b| { - llvm::LLVMPassManagerBuilderPopulateFunctionPassManager(b, fpm); - llvm::LLVMPassManagerBuilderPopulateModulePassManager(b, mpm); - }) - } - - for pass in &config.passes { - if !addpass(pass) { - diag_handler.warn(&format!("unknown pass `{}`, ignoring", - pass)); - } - } - - for pass in &cgcx.plugin_passes { - if !addpass(pass) { - diag_handler.err(&format!("a plugin asked for LLVM pass \ - `{}` but LLVM does not \ - recognize it", pass)); - } - } - - diag_handler.abort_if_errors(); - - // Finally, run the actual optimization passes - time_ext(config.time_passes, - None, - &format!("llvm function passes [{}]", module_name.unwrap()), - || { - llvm::LLVMRustRunFunctionPassManager(fpm, llmod) - }); - timeline.record("fpm"); - time_ext(config.time_passes, - None, - &format!("llvm module passes [{}]", module_name.unwrap()), - || { - llvm::LLVMRunPassManager(mpm, llmod) - }); - - // Deallocate managers that we're now done with - llvm::LLVMDisposePassManager(fpm); - llvm::LLVMDisposePassManager(mpm); - } - Ok(()) -} - -fn generate_lto_work(cgcx: &CodegenContext, - modules: Vec) - -> Vec<(WorkItem, u64)> -{ - let mut timeline = cgcx.time_graph.as_ref().map(|tg| { - tg.start(TRANS_WORKER_TIMELINE, - TRANS_WORK_PACKAGE_KIND, - "generate lto") - }).unwrap_or(Timeline::noop()); - let lto_modules = lto::run(cgcx, modules, &mut timeline) - .unwrap_or_else(|e| e.raise()); - - lto_modules.into_iter().map(|module| { - let cost = module.cost(); - (WorkItem::LTO(module), cost) - }).collect() -} - -unsafe fn codegen(cgcx: &CodegenContext, - diag_handler: &Handler, - mtrans: ModuleTranslation, - config: &ModuleConfig, - timeline: &mut Timeline) - -> Result -{ - timeline.record("codegen"); - let (llmod, llcx, tm) = match mtrans.source { - ModuleSource::Translated(ref llvm) => (llvm.llmod, llvm.llcx, llvm.tm), - ModuleSource::Preexisting(_) => { - bug!("codegen: called with ModuleSource::Preexisting") - } - }; - let module_name = mtrans.name.clone(); - let module_name = Some(&module_name[..]); - let handlers = DiagnosticHandlers::new(cgcx, diag_handler, llcx); - - if cgcx.msvc_imps_needed { - create_msvc_imps(cgcx, llcx, llmod); - } - - // A codegen-specific pass manager is used to generate object - // files for an LLVM module. - // - // Apparently each of these pass managers is a one-shot kind of - // thing, so we create a new one for each type of output. The - // pass manager passed to the closure should be ensured to not - // escape the closure itself, and the manager should only be - // used once. - unsafe fn with_codegen(tm: TargetMachineRef, - llmod: ModuleRef, - no_builtins: bool, - f: F) -> R - where F: FnOnce(PassManagerRef) -> R, - { - let cpm = llvm::LLVMCreatePassManager(); - llvm::LLVMRustAddAnalysisPasses(tm, cpm, llmod); - llvm::LLVMRustAddLibraryInfo(cpm, llmod, no_builtins); - f(cpm) - } - - // If we don't have the integrated assembler, then we need to emit asm - // from LLVM and use `gcc` to create the object file. - let asm_to_obj = config.emit_obj && config.no_integrated_as; - - // Change what we write and cleanup based on whether obj files are - // just llvm bitcode. In that case write bitcode, and possibly - // delete the bitcode if it wasn't requested. Don't generate the - // machine code, instead copy the .o file from the .bc - let write_bc = config.emit_bc || config.obj_is_bitcode; - let rm_bc = !config.emit_bc && config.obj_is_bitcode; - let write_obj = config.emit_obj && !config.obj_is_bitcode && !asm_to_obj; - let copy_bc_to_obj = config.emit_obj && config.obj_is_bitcode; - - let bc_out = cgcx.output_filenames.temp_path(OutputType::Bitcode, module_name); - let obj_out = cgcx.output_filenames.temp_path(OutputType::Object, module_name); - - - if write_bc || config.emit_bc_compressed || config.embed_bitcode { - let thin; - let old; - let data = if llvm::LLVMRustThinLTOAvailable() { - thin = ThinBuffer::new(llmod); - thin.data() - } else { - old = ModuleBuffer::new(llmod); - old.data() - }; - timeline.record("make-bc"); - - if write_bc { - if let Err(e) = fs::write(&bc_out, data) { - diag_handler.err(&format!("failed to write bytecode: {}", e)); - } - timeline.record("write-bc"); - } - - if config.embed_bitcode { - embed_bitcode(cgcx, llcx, llmod, Some(data)); - timeline.record("embed-bc"); - } - - if config.emit_bc_compressed { - let dst = bc_out.with_extension(RLIB_BYTECODE_EXTENSION); - let data = bytecode::encode(&mtrans.llmod_id, data); - if let Err(e) = fs::write(&dst, data) { - diag_handler.err(&format!("failed to write bytecode: {}", e)); - } - timeline.record("compress-bc"); - } - } else if config.embed_bitcode_marker { - embed_bitcode(cgcx, llcx, llmod, None); - } - - time_ext(config.time_passes, None, &format!("codegen passes [{}]", module_name.unwrap()), - || -> Result<(), FatalError> { - if config.emit_ir { - let out = cgcx.output_filenames.temp_path(OutputType::LlvmAssembly, module_name); - let out = path2cstr(&out); - - extern "C" fn demangle_callback(input_ptr: *const c_char, - input_len: size_t, - output_ptr: *mut c_char, - output_len: size_t) -> size_t { - let input = unsafe { - slice::from_raw_parts(input_ptr as *const u8, input_len as usize) - }; - - let input = match str::from_utf8(input) { - Ok(s) => s, - Err(_) => return 0, - }; - - let output = unsafe { - slice::from_raw_parts_mut(output_ptr as *mut u8, output_len as usize) - }; - let mut cursor = io::Cursor::new(output); - - let demangled = match rustc_demangle::try_demangle(input) { - Ok(d) => d, - Err(_) => return 0, - }; - - if let Err(_) = write!(cursor, "{:#}", demangled) { - // Possible only if provided buffer is not big enough - return 0; - } - - cursor.position() as size_t - } - - with_codegen(tm, llmod, config.no_builtins, |cpm| { - llvm::LLVMRustPrintModule(cpm, llmod, out.as_ptr(), demangle_callback); - llvm::LLVMDisposePassManager(cpm); - }); - timeline.record("ir"); - } - - if config.emit_asm || asm_to_obj { - let path = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - - // We can't use the same module for asm and binary output, because that triggers - // various errors like invalid IR or broken binaries, so we might have to clone the - // module to produce the asm output - let llmod = if config.emit_obj { - llvm::LLVMCloneModule(llmod) - } else { - llmod - }; - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &path, - llvm::FileType::AssemblyFile) - })?; - if config.emit_obj { - llvm::LLVMDisposeModule(llmod); - } - timeline.record("asm"); - } - - if write_obj { - with_codegen(tm, llmod, config.no_builtins, |cpm| { - write_output_file(diag_handler, tm, cpm, llmod, &obj_out, - llvm::FileType::ObjectFile) - })?; - timeline.record("obj"); - } else if asm_to_obj { - let assembly = cgcx.output_filenames.temp_path(OutputType::Assembly, module_name); - run_assembler(cgcx, diag_handler, &assembly, &obj_out); - timeline.record("asm_to_obj"); - - if !config.emit_asm && !cgcx.save_temps { - drop(fs::remove_file(&assembly)); - } - } - - Ok(()) - })?; - - if copy_bc_to_obj { - debug!("copying bitcode {:?} to obj {:?}", bc_out, obj_out); - if let Err(e) = link_or_copy(&bc_out, &obj_out) { - diag_handler.err(&format!("failed to copy bitcode to object file: {}", e)); - } - } - - if rm_bc { - debug!("removing_bitcode {:?}", bc_out); - if let Err(e) = fs::remove_file(&bc_out) { - diag_handler.err(&format!("failed to remove bitcode: {}", e)); - } - } - - drop(handlers); - Ok(mtrans.into_compiled_module(config.emit_obj, - config.emit_bc, - config.emit_bc_compressed, - &cgcx.output_filenames)) -} - -/// Embed the bitcode of an LLVM module in the LLVM module itself. -/// -/// This is done primarily for iOS where it appears to be standard to compile C -/// code at least with `-fembed-bitcode` which creates two sections in the -/// executable: -/// -/// * __LLVM,__bitcode -/// * __LLVM,__cmdline -/// -/// It appears *both* of these sections are necessary to get the linker to -/// recognize what's going on. For us though we just always throw in an empty -/// cmdline section. -/// -/// Furthermore debug/O1 builds don't actually embed bitcode but rather just -/// embed an empty section. -/// -/// Basically all of this is us attempting to follow in the footsteps of clang -/// on iOS. See #35968 for lots more info. -unsafe fn embed_bitcode(cgcx: &CodegenContext, - llcx: ContextRef, - llmod: ModuleRef, - bitcode: Option<&[u8]>) { - let llconst = C_bytes_in_context(llcx, bitcode.unwrap_or(&[])); - let llglobal = llvm::LLVMAddGlobal( - llmod, - val_ty(llconst).to_ref(), - "rustc.embedded.module\0".as_ptr() as *const _, - ); - llvm::LLVMSetInitializer(llglobal, llconst); - - let is_apple = cgcx.opts.target_triple.triple().contains("-ios") || - cgcx.opts.target_triple.triple().contains("-darwin"); - - let section = if is_apple { - "__LLVM,__bitcode\0" - } else { - ".llvmbc\0" - }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); - llvm::LLVMSetGlobalConstant(llglobal, llvm::True); - - let llconst = C_bytes_in_context(llcx, &[]); - let llglobal = llvm::LLVMAddGlobal( - llmod, - val_ty(llconst).to_ref(), - "rustc.embedded.cmdline\0".as_ptr() as *const _, - ); - llvm::LLVMSetInitializer(llglobal, llconst); - let section = if is_apple { - "__LLVM,__cmdline\0" - } else { - ".llvmcmd\0" - }; - llvm::LLVMSetSection(llglobal, section.as_ptr() as *const _); - llvm::LLVMRustSetLinkage(llglobal, llvm::Linkage::PrivateLinkage); -} - -pub(crate) struct CompiledModules { - pub modules: Vec, - pub metadata_module: CompiledModule, - pub allocator_module: Option, -} - -fn need_crate_bitcode_for_rlib(sess: &Session) -> bool { - sess.crate_types.borrow().contains(&config::CrateTypeRlib) && - sess.opts.output_types.contains_key(&OutputType::Exe) -} - -pub fn start_async_translation(tcx: TyCtxt, - time_graph: Option, - link: LinkMeta, - metadata: EncodedMetadata, - coordinator_receive: Receiver>, - total_cgus: usize) - -> OngoingCrateTranslation { - let sess = tcx.sess; - let crate_name = tcx.crate_name(LOCAL_CRATE); - let no_builtins = attr::contains_name(&tcx.hir.krate().attrs, "no_builtins"); - let subsystem = attr::first_attr_value_str_by_name(&tcx.hir.krate().attrs, - "windows_subsystem"); - let windows_subsystem = subsystem.map(|subsystem| { - if subsystem != "windows" && subsystem != "console" { - tcx.sess.fatal(&format!("invalid windows subsystem `{}`, only \ - `windows` and `console` are allowed", - subsystem)); - } - subsystem.to_string() - }); - - let linker_info = LinkerInfo::new(tcx); - let crate_info = CrateInfo::new(tcx); - - // Figure out what we actually need to build. - let mut modules_config = ModuleConfig::new(sess.opts.cg.passes.clone()); - let mut metadata_config = ModuleConfig::new(vec![]); - let mut allocator_config = ModuleConfig::new(vec![]); - - if let Some(ref sanitizer) = sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - modules_config.passes.push("asan".to_owned()); - modules_config.passes.push("asan-module".to_owned()); - } - Sanitizer::Memory => { - modules_config.passes.push("msan".to_owned()) - } - Sanitizer::Thread => { - modules_config.passes.push("tsan".to_owned()) - } - _ => {} - } - } - - if sess.opts.debugging_opts.profile { - modules_config.passes.push("insert-gcov-profiling".to_owned()) - } - - modules_config.pgo_gen = sess.opts.debugging_opts.pgo_gen.clone(); - modules_config.pgo_use = sess.opts.debugging_opts.pgo_use.clone(); - - modules_config.opt_level = Some(get_llvm_opt_level(sess.opts.optimize)); - modules_config.opt_size = Some(get_llvm_opt_size(sess.opts.optimize)); - - // Save all versions of the bytecode if we're saving our temporaries. - if sess.opts.cg.save_temps { - modules_config.emit_no_opt_bc = true; - modules_config.emit_bc = true; - modules_config.emit_lto_bc = true; - metadata_config.emit_bc = true; - allocator_config.emit_bc = true; - } - - // Emit compressed bitcode files for the crate if we're emitting an rlib. - // Whenever an rlib is created, the bitcode is inserted into the archive in - // order to allow LTO against it. - if need_crate_bitcode_for_rlib(sess) { - modules_config.emit_bc_compressed = true; - allocator_config.emit_bc_compressed = true; - } - - modules_config.no_integrated_as = tcx.sess.opts.cg.no_integrated_as || - tcx.sess.target.target.options.no_integrated_as; - - for output_type in sess.opts.output_types.keys() { - match *output_type { - OutputType::Bitcode => { modules_config.emit_bc = true; } - OutputType::LlvmAssembly => { modules_config.emit_ir = true; } - OutputType::Assembly => { - modules_config.emit_asm = true; - // If we're not using the LLVM assembler, this function - // could be invoked specially with output_type_assembly, so - // in this case we still want the metadata object file. - if !sess.opts.output_types.contains_key(&OutputType::Assembly) { - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - } - } - OutputType::Object => { modules_config.emit_obj = true; } - OutputType::Metadata => { metadata_config.emit_obj = true; } - OutputType::Exe => { - modules_config.emit_obj = true; - metadata_config.emit_obj = true; - allocator_config.emit_obj = true; - }, - OutputType::Mir => {} - OutputType::DepInfo => {} - } - } - - modules_config.set_flags(sess, no_builtins); - metadata_config.set_flags(sess, no_builtins); - allocator_config.set_flags(sess, no_builtins); - - // Exclude metadata and allocator modules from time_passes output, since - // they throw off the "LLVM passes" measurement. - metadata_config.time_passes = false; - allocator_config.time_passes = false; - - let (shared_emitter, shared_emitter_main) = SharedEmitter::new(); - let (trans_worker_send, trans_worker_receive) = channel(); - - let coordinator_thread = start_executing_work(tcx, - &crate_info, - shared_emitter, - trans_worker_send, - coordinator_receive, - total_cgus, - sess.jobserver.clone(), - time_graph.clone(), - Arc::new(modules_config), - Arc::new(metadata_config), - Arc::new(allocator_config)); - - OngoingCrateTranslation { - crate_name, - link, - metadata, - windows_subsystem, - linker_info, - crate_info, - - time_graph, - coordinator_send: tcx.tx_to_llvm_workers.lock().clone(), - trans_worker_receive, - shared_emitter_main, - future: coordinator_thread, - output_filenames: tcx.output_filenames(LOCAL_CRATE), - } -} - -fn copy_all_cgu_workproducts_to_incr_comp_cache_dir( - sess: &Session, - compiled_modules: &CompiledModules -) -> FxHashMap { - let mut work_products = FxHashMap::default(); - - if sess.opts.incremental.is_none() { - return work_products; - } - - for module in compiled_modules.modules.iter() { - let mut files = vec![]; - - if let Some(ref path) = module.object { - files.push((WorkProductFileKind::Object, path.clone())); - } - if let Some(ref path) = module.bytecode { - files.push((WorkProductFileKind::Bytecode, path.clone())); - } - if let Some(ref path) = module.bytecode_compressed { - files.push((WorkProductFileKind::BytecodeCompressed, path.clone())); - } - - if let Some((id, product)) = - copy_cgu_workproducts_to_incr_comp_cache_dir(sess, &module.name, &files) { - work_products.insert(id, product); - } - } - - work_products -} - -fn produce_final_output_artifacts(sess: &Session, - compiled_modules: &CompiledModules, - crate_output: &OutputFilenames) { - let mut user_wants_bitcode = false; - let mut user_wants_objects = false; - - // Produce final compile outputs. - let copy_gracefully = |from: &Path, to: &Path| { - if let Err(e) = fs::copy(from, to) { - sess.err(&format!("could not copy {:?} to {:?}: {}", from, to, e)); - } - }; - - let copy_if_one_unit = |output_type: OutputType, - keep_numbered: bool| { - if compiled_modules.modules.len() == 1 { - // 1) Only one codegen unit. In this case it's no difficulty - // to copy `foo.0.x` to `foo.x`. - let module_name = Some(&compiled_modules.modules[0].name[..]); - let path = crate_output.temp_path(output_type, module_name); - copy_gracefully(&path, - &crate_output.path(output_type)); - if !sess.opts.cg.save_temps && !keep_numbered { - // The user just wants `foo.x`, not `foo.#module-name#.x`. - remove(sess, &path); - } - } else { - let ext = crate_output.temp_path(output_type, None) - .extension() - .unwrap() - .to_str() - .unwrap() - .to_owned(); - - if crate_output.outputs.contains_key(&output_type) { - // 2) Multiple codegen units, with `--emit foo=some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring emit path because multiple .{} files \ - were produced", ext)); - } else if crate_output.single_output_file.is_some() { - // 3) Multiple codegen units, with `-o some_name`. We have - // no good solution for this case, so warn the user. - sess.warn(&format!("ignoring -o because multiple .{} files \ - were produced", ext)); - } else { - // 4) Multiple codegen units, but no explicit name. We - // just leave the `foo.0.x` files in place. - // (We don't have to do any work in this case.) - } - } - }; - - // Flag to indicate whether the user explicitly requested bitcode. - // Otherwise, we produced it only as a temporary output, and will need - // to get rid of it. - for output_type in crate_output.outputs.keys() { - match *output_type { - OutputType::Bitcode => { - user_wants_bitcode = true; - // Copy to .bc, but always keep the .0.bc. There is a later - // check to figure out if we should delete .0.bc files, or keep - // them for making an rlib. - copy_if_one_unit(OutputType::Bitcode, true); - } - OutputType::LlvmAssembly => { - copy_if_one_unit(OutputType::LlvmAssembly, false); - } - OutputType::Assembly => { - copy_if_one_unit(OutputType::Assembly, false); - } - OutputType::Object => { - user_wants_objects = true; - copy_if_one_unit(OutputType::Object, true); - } - OutputType::Mir | - OutputType::Metadata | - OutputType::Exe | - OutputType::DepInfo => {} - } - } - - // Clean up unwanted temporary files. - - // We create the following files by default: - // - #crate#.#module-name#.bc - // - #crate#.#module-name#.o - // - #crate#.crate.metadata.bc - // - #crate#.crate.metadata.o - // - #crate#.o (linked from crate.##.o) - // - #crate#.bc (copied from crate.##.bc) - // We may create additional files if requested by the user (through - // `-C save-temps` or `--emit=` flags). - - if !sess.opts.cg.save_temps { - // Remove the temporary .#module-name#.o objects. If the user didn't - // explicitly request bitcode (with --emit=bc), and the bitcode is not - // needed for building an rlib, then we must remove .#module-name#.bc as - // well. - - // Specific rules for keeping .#module-name#.bc: - // - If the user requested bitcode (`user_wants_bitcode`), and - // codegen_units > 1, then keep it. - // - If the user requested bitcode but codegen_units == 1, then we - // can toss .#module-name#.bc because we copied it to .bc earlier. - // - If we're not building an rlib and the user didn't request - // bitcode, then delete .#module-name#.bc. - // If you change how this works, also update back::link::link_rlib, - // where .#module-name#.bc files are (maybe) deleted after making an - // rlib. - let needs_crate_object = crate_output.outputs.contains_key(&OutputType::Exe); - - let keep_numbered_bitcode = user_wants_bitcode && sess.codegen_units() > 1; - - let keep_numbered_objects = needs_crate_object || - (user_wants_objects && sess.codegen_units() > 1); - - for module in compiled_modules.modules.iter() { - if let Some(ref path) = module.object { - if !keep_numbered_objects { - remove(sess, path); - } - } - - if let Some(ref path) = module.bytecode { - if !keep_numbered_bitcode { - remove(sess, path); - } - } - } - - if !user_wants_bitcode { - if let Some(ref path) = compiled_modules.metadata_module.bytecode { - remove(sess, &path); - } - - if let Some(ref allocator_module) = compiled_modules.allocator_module { - if let Some(ref path) = allocator_module.bytecode { - remove(sess, path); - } - } - } - } - - // We leave the following files around by default: - // - #crate#.o - // - #crate#.crate.metadata.o - // - #crate#.bc - // These are used in linking steps and will be cleaned up afterward. -} - -pub(crate) fn dump_incremental_data(trans: &CrateTranslation) { - println!("[incremental] Re-using {} out of {} modules", - trans.modules.iter().filter(|m| m.pre_existing).count(), - trans.modules.len()); -} - -enum WorkItem { - Optimize(ModuleTranslation), - LTO(lto::LtoModuleTranslation), -} - -impl WorkItem { - fn kind(&self) -> ModuleKind { - match *self { - WorkItem::Optimize(ref m) => m.kind, - WorkItem::LTO(_) => ModuleKind::Regular, - } - } - - fn name(&self) -> String { - match *self { - WorkItem::Optimize(ref m) => format!("optimize: {}", m.name), - WorkItem::LTO(ref m) => format!("lto: {}", m.name()), - } - } -} - -enum WorkItemResult { - Compiled(CompiledModule), - NeedsLTO(ModuleTranslation), -} - -fn execute_work_item(cgcx: &CodegenContext, - work_item: WorkItem, - timeline: &mut Timeline) - -> Result -{ - let diag_handler = cgcx.create_diag_handler(); - let config = cgcx.config(work_item.kind()); - let mtrans = match work_item { - WorkItem::Optimize(mtrans) => mtrans, - WorkItem::LTO(mut lto) => { - unsafe { - let module = lto.optimize(cgcx, timeline)?; - let module = codegen(cgcx, &diag_handler, module, config, timeline)?; - return Ok(WorkItemResult::Compiled(module)) - } - } - }; - let module_name = mtrans.name.clone(); - - let pre_existing = match mtrans.source { - ModuleSource::Translated(_) => None, - ModuleSource::Preexisting(ref wp) => Some(wp.clone()), - }; - - if let Some(wp) = pre_existing { - let incr_comp_session_dir = cgcx.incr_comp_session_dir - .as_ref() - .unwrap(); - let name = &mtrans.name; - let mut object = None; - let mut bytecode = None; - let mut bytecode_compressed = None; - for (kind, saved_file) in wp.saved_files { - let obj_out = match kind { - WorkProductFileKind::Object => { - let path = cgcx.output_filenames.temp_path(OutputType::Object, Some(name)); - object = Some(path.clone()); - path - } - WorkProductFileKind::Bytecode => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)); - bytecode = Some(path.clone()); - path - } - WorkProductFileKind::BytecodeCompressed => { - let path = cgcx.output_filenames.temp_path(OutputType::Bitcode, Some(name)) - .with_extension(RLIB_BYTECODE_EXTENSION); - bytecode_compressed = Some(path.clone()); - path - } - }; - let source_file = in_incr_comp_dir(&incr_comp_session_dir, - &saved_file); - debug!("copying pre-existing module `{}` from {:?} to {}", - mtrans.name, - source_file, - obj_out.display()); - match link_or_copy(&source_file, &obj_out) { - Ok(_) => { } - Err(err) => { - diag_handler.err(&format!("unable to copy {} to {}: {}", - source_file.display(), - obj_out.display(), - err)); - } - } - } - assert_eq!(object.is_some(), config.emit_obj); - assert_eq!(bytecode.is_some(), config.emit_bc); - assert_eq!(bytecode_compressed.is_some(), config.emit_bc_compressed); - - Ok(WorkItemResult::Compiled(CompiledModule { - llmod_id: mtrans.llmod_id.clone(), - name: module_name, - kind: ModuleKind::Regular, - pre_existing: true, - object, - bytecode, - bytecode_compressed, - })) - } else { - debug!("llvm-optimizing {:?}", module_name); - - unsafe { - optimize(cgcx, &diag_handler, &mtrans, config, timeline)?; - - // After we've done the initial round of optimizations we need to - // decide whether to synchronously codegen this module or ship it - // back to the coordinator thread for further LTO processing (which - // has to wait for all the initial modules to be optimized). - // - // Here we dispatch based on the `cgcx.lto` and kind of module we're - // translating... - let needs_lto = match cgcx.lto { - Lto::No => false, - - // Here we've got a full crate graph LTO requested. We ignore - // this, however, if the crate type is only an rlib as there's - // no full crate graph to process, that'll happen later. - // - // This use case currently comes up primarily for targets that - // require LTO so the request for LTO is always unconditionally - // passed down to the backend, but we don't actually want to do - // anything about it yet until we've got a final product. - Lto::Yes | Lto::Fat | Lto::Thin => { - cgcx.crate_types.len() != 1 || - cgcx.crate_types[0] != config::CrateTypeRlib - } - - // When we're automatically doing ThinLTO for multi-codegen-unit - // builds we don't actually want to LTO the allocator modules if - // it shows up. This is due to various linker shenanigans that - // we'll encounter later. - // - // Additionally here's where we also factor in the current LLVM - // version. If it doesn't support ThinLTO we skip this. - Lto::ThinLocal => { - mtrans.kind != ModuleKind::Allocator && - llvm::LLVMRustThinLTOAvailable() - } - }; - - // Metadata modules never participate in LTO regardless of the lto - // settings. - let needs_lto = needs_lto && mtrans.kind != ModuleKind::Metadata; - - // Don't run LTO passes when cross-lang LTO is enabled. The linker - // will do that for us in this case. - let needs_lto = needs_lto && - !cgcx.opts.debugging_opts.cross_lang_lto.embed_bitcode(); - - if needs_lto { - Ok(WorkItemResult::NeedsLTO(mtrans)) - } else { - let module = codegen(cgcx, &diag_handler, mtrans, config, timeline)?; - Ok(WorkItemResult::Compiled(module)) - } - } - } -} - -enum Message { - Token(io::Result), - NeedsLTO { - result: ModuleTranslation, - worker_id: usize, - }, - Done { - result: Result, - worker_id: usize, - }, - TranslationDone { - llvm_work_item: WorkItem, - cost: u64, - }, - TranslationComplete, - TranslateItem, -} - -struct Diagnostic { - msg: String, - code: Option, - lvl: Level, -} - -#[derive(PartialEq, Clone, Copy, Debug)] -enum MainThreadWorkerState { - Idle, - Translating, - LLVMing, -} - -fn start_executing_work(tcx: TyCtxt, - crate_info: &CrateInfo, - shared_emitter: SharedEmitter, - trans_worker_send: Sender, - coordinator_receive: Receiver>, - total_cgus: usize, - jobserver: Client, - time_graph: Option, - modules_config: Arc, - metadata_config: Arc, - allocator_config: Arc) - -> thread::JoinHandle> { - let coordinator_send = tcx.tx_to_llvm_workers.lock().clone(); - let sess = tcx.sess; - - // Compute the set of symbols we need to retain when doing LTO (if we need to) - let exported_symbols = { - let mut exported_symbols = FxHashMap(); - - let copy_symbols = |cnum| { - let symbols = tcx.exported_symbols(cnum) - .iter() - .map(|&(s, lvl)| (s.symbol_name(tcx).to_string(), lvl)) - .collect(); - Arc::new(symbols) - }; - - match sess.lto() { - Lto::No => None, - Lto::ThinLocal => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - Some(Arc::new(exported_symbols)) - } - Lto::Yes | Lto::Fat | Lto::Thin => { - exported_symbols.insert(LOCAL_CRATE, copy_symbols(LOCAL_CRATE)); - for &cnum in tcx.crates().iter() { - exported_symbols.insert(cnum, copy_symbols(cnum)); - } - Some(Arc::new(exported_symbols)) - } - } - }; - - // First up, convert our jobserver into a helper thread so we can use normal - // mpsc channels to manage our messages and such. - // After we've requested tokens then we'll, when we can, - // get tokens on `coordinator_receive` which will - // get managed in the main loop below. - let coordinator_send2 = coordinator_send.clone(); - let helper = jobserver.into_helper_thread(move |token| { - drop(coordinator_send2.send(Box::new(Message::Token(token)))); - }).expect("failed to spawn helper thread"); - - let mut each_linked_rlib_for_lto = Vec::new(); - drop(link::each_linked_rlib(sess, crate_info, &mut |cnum, path| { - if link::ignored_for_lto(sess, crate_info, cnum) { - return - } - each_linked_rlib_for_lto.push((cnum, path.to_path_buf())); - })); - - let assembler_cmd = if modules_config.no_integrated_as { - // HACK: currently we use linker (gcc) as our assembler - let (name, mut cmd) = get_linker(sess); - cmd.args(&sess.target.target.options.asm_args); - Some(Arc::new(AssemblerCommand { - name, - cmd, - })) - } else { - None - }; - - let cgcx = CodegenContext { - crate_types: sess.crate_types.borrow().clone(), - each_linked_rlib_for_lto, - lto: sess.lto(), - no_landing_pads: sess.no_landing_pads(), - fewer_names: sess.fewer_names(), - save_temps: sess.opts.cg.save_temps, - opts: Arc::new(sess.opts.clone()), - time_passes: sess.time_passes(), - exported_symbols, - plugin_passes: sess.plugin_llvm_passes.borrow().clone(), - remark: sess.opts.cg.remark.clone(), - worker: 0, - incr_comp_session_dir: sess.incr_comp_session_dir_opt().map(|r| r.clone()), - coordinator_send, - diag_emitter: shared_emitter.clone(), - time_graph, - output_filenames: tcx.output_filenames(LOCAL_CRATE), - regular_module_config: modules_config, - metadata_module_config: metadata_config, - allocator_module_config: allocator_config, - tm_factory: target_machine_factory(tcx.sess, false), - total_cgus, - msvc_imps_needed: msvc_imps_needed(tcx), - target_pointer_width: tcx.sess.target.target.target_pointer_width.clone(), - debuginfo: tcx.sess.opts.debuginfo, - assembler_cmd, - }; - - // This is the "main loop" of parallel work happening for parallel codegen. - // It's here that we manage parallelism, schedule work, and work with - // messages coming from clients. - // - // There are a few environmental pre-conditions that shape how the system - // is set up: - // - // - Error reporting only can happen on the main thread because that's the - // only place where we have access to the compiler `Session`. - // - LLVM work can be done on any thread. - // - Translation can only happen on the main thread. - // - Each thread doing substantial work most be in possession of a `Token` - // from the `Jobserver`. - // - The compiler process always holds one `Token`. Any additional `Tokens` - // have to be requested from the `Jobserver`. - // - // Error Reporting - // =============== - // The error reporting restriction is handled separately from the rest: We - // set up a `SharedEmitter` the holds an open channel to the main thread. - // When an error occurs on any thread, the shared emitter will send the - // error message to the receiver main thread (`SharedEmitterMain`). The - // main thread will periodically query this error message queue and emit - // any error messages it has received. It might even abort compilation if - // has received a fatal error. In this case we rely on all other threads - // being torn down automatically with the main thread. - // Since the main thread will often be busy doing translation work, error - // reporting will be somewhat delayed, since the message queue can only be - // checked in between to work packages. - // - // Work Processing Infrastructure - // ============================== - // The work processing infrastructure knows three major actors: - // - // - the coordinator thread, - // - the main thread, and - // - LLVM worker threads - // - // The coordinator thread is running a message loop. It instructs the main - // thread about what work to do when, and it will spawn off LLVM worker - // threads as open LLVM WorkItems become available. - // - // The job of the main thread is to translate CGUs into LLVM work package - // (since the main thread is the only thread that can do this). The main - // thread will block until it receives a message from the coordinator, upon - // which it will translate one CGU, send it to the coordinator and block - // again. This way the coordinator can control what the main thread is - // doing. - // - // The coordinator keeps a queue of LLVM WorkItems, and when a `Token` is - // available, it will spawn off a new LLVM worker thread and let it process - // that a WorkItem. When a LLVM worker thread is done with its WorkItem, - // it will just shut down, which also frees all resources associated with - // the given LLVM module, and sends a message to the coordinator that the - // has been completed. - // - // Work Scheduling - // =============== - // The scheduler's goal is to minimize the time it takes to complete all - // work there is, however, we also want to keep memory consumption low - // if possible. These two goals are at odds with each other: If memory - // consumption were not an issue, we could just let the main thread produce - // LLVM WorkItems at full speed, assuring maximal utilization of - // Tokens/LLVM worker threads. However, since translation usual is faster - // than LLVM processing, the queue of LLVM WorkItems would fill up and each - // WorkItem potentially holds on to a substantial amount of memory. - // - // So the actual goal is to always produce just enough LLVM WorkItems as - // not to starve our LLVM worker threads. That means, once we have enough - // WorkItems in our queue, we can block the main thread, so it does not - // produce more until we need them. - // - // Doing LLVM Work on the Main Thread - // ---------------------------------- - // Since the main thread owns the compiler processes implicit `Token`, it is - // wasteful to keep it blocked without doing any work. Therefore, what we do - // in this case is: We spawn off an additional LLVM worker thread that helps - // reduce the queue. The work it is doing corresponds to the implicit - // `Token`. The coordinator will mark the main thread as being busy with - // LLVM work. (The actual work happens on another OS thread but we just care - // about `Tokens`, not actual threads). - // - // When any LLVM worker thread finishes while the main thread is marked as - // "busy with LLVM work", we can do a little switcheroo: We give the Token - // of the just finished thread to the LLVM worker thread that is working on - // behalf of the main thread's implicit Token, thus freeing up the main - // thread again. The coordinator can then again decide what the main thread - // should do. This allows the coordinator to make decisions at more points - // in time. - // - // Striking a Balance between Throughput and Memory Consumption - // ------------------------------------------------------------ - // Since our two goals, (1) use as many Tokens as possible and (2) keep - // memory consumption as low as possible, are in conflict with each other, - // we have to find a trade off between them. Right now, the goal is to keep - // all workers busy, which means that no worker should find the queue empty - // when it is ready to start. - // How do we do achieve this? Good question :) We actually never know how - // many `Tokens` are potentially available so it's hard to say how much to - // fill up the queue before switching the main thread to LLVM work. Also we - // currently don't have a means to estimate how long a running LLVM worker - // will still be busy with it's current WorkItem. However, we know the - // maximal count of available Tokens that makes sense (=the number of CPU - // cores), so we can take a conservative guess. The heuristic we use here - // is implemented in the `queue_full_enough()` function. - // - // Some Background on Jobservers - // ----------------------------- - // It's worth also touching on the management of parallelism here. We don't - // want to just spawn a thread per work item because while that's optimal - // parallelism it may overload a system with too many threads or violate our - // configuration for the maximum amount of cpu to use for this process. To - // manage this we use the `jobserver` crate. - // - // Job servers are an artifact of GNU make and are used to manage - // parallelism between processes. A jobserver is a glorified IPC semaphore - // basically. Whenever we want to run some work we acquire the semaphore, - // and whenever we're done with that work we release the semaphore. In this - // manner we can ensure that the maximum number of parallel workers is - // capped at any one point in time. - // - // LTO and the coordinator thread - // ------------------------------ - // - // The final job the coordinator thread is responsible for is managing LTO - // and how that works. When LTO is requested what we'll to is collect all - // optimized LLVM modules into a local vector on the coordinator. Once all - // modules have been translated and optimized we hand this to the `lto` - // module for further optimization. The `lto` module will return back a list - // of more modules to work on, which the coordinator will continue to spawn - // work for. - // - // Each LLVM module is automatically sent back to the coordinator for LTO if - // necessary. There's already optimizations in place to avoid sending work - // back to the coordinator if LTO isn't requested. - return thread::spawn(move || { - // We pretend to be within the top-level LLVM time-passes task here: - set_time_depth(1); - - let max_workers = ::num_cpus::get(); - let mut worker_id_counter = 0; - let mut free_worker_ids = Vec::new(); - let mut get_worker_id = |free_worker_ids: &mut Vec| { - if let Some(id) = free_worker_ids.pop() { - id - } else { - let id = worker_id_counter; - worker_id_counter += 1; - id - } - }; - - // This is where we collect codegen units that have gone all the way - // through translation and LLVM. - let mut compiled_modules = vec![]; - let mut compiled_metadata_module = None; - let mut compiled_allocator_module = None; - let mut needs_lto = Vec::new(); - let mut started_lto = false; - - // This flag tracks whether all items have gone through translations - let mut translation_done = false; - - // This is the queue of LLVM work items that still need processing. - let mut work_items = Vec::<(WorkItem, u64)>::new(); - - // This are the Jobserver Tokens we currently hold. Does not include - // the implicit Token the compiler process owns no matter what. - let mut tokens = Vec::new(); - - let mut main_thread_worker_state = MainThreadWorkerState::Idle; - let mut running = 0; - - let mut llvm_start_time = None; - - // Run the message loop while there's still anything that needs message - // processing: - while !translation_done || - work_items.len() > 0 || - running > 0 || - needs_lto.len() > 0 || - main_thread_worker_state != MainThreadWorkerState::Idle { - - // While there are still CGUs to be translated, the coordinator has - // to decide how to utilize the compiler processes implicit Token: - // For translating more CGU or for running them through LLVM. - if !translation_done { - if main_thread_worker_state == MainThreadWorkerState::Idle { - if !queue_full_enough(work_items.len(), running, max_workers) { - // The queue is not full enough, translate more items: - if let Err(_) = trans_worker_send.send(Message::TranslateItem) { - panic!("Could not send Message::TranslateItem to main thread") - } - main_thread_worker_state = MainThreadWorkerState::Translating; - } else { - // The queue is full enough to not let the worker - // threads starve. Use the implicit Token to do some - // LLVM work too. - let (item, _) = work_items.pop() - .expect("queue empty - queue_full_enough() broken?"); - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); - } - } - } else { - // If we've finished everything related to normal translation - // then it must be the case that we've got some LTO work to do. - // Perform the serial work here of figuring out what we're - // going to LTO and then push a bunch of work items onto our - // queue to do LTO - if work_items.len() == 0 && - running == 0 && - main_thread_worker_state == MainThreadWorkerState::Idle { - assert!(!started_lto); - assert!(needs_lto.len() > 0); - started_lto = true; - let modules = mem::replace(&mut needs_lto, Vec::new()); - for (work, cost) in generate_lto_work(&cgcx, modules) { - let insertion_index = work_items - .binary_search_by_key(&cost, |&(_, cost)| cost) - .unwrap_or_else(|e| e); - work_items.insert(insertion_index, (work, cost)); - helper.request_token(); - } - } - - // In this branch, we know that everything has been translated, - // so it's just a matter of determining whether the implicit - // Token is free to use for LLVM work. - match main_thread_worker_state { - MainThreadWorkerState::Idle => { - if let Some((item, _)) = work_items.pop() { - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - main_thread_worker_state = MainThreadWorkerState::LLVMing; - spawn_work(cgcx, item); - } else { - // There is no unstarted work, so let the main thread - // take over for a running worker. Otherwise the - // implicit token would just go to waste. - // We reduce the `running` counter by one. The - // `tokens.truncate()` below will take care of - // giving the Token back. - debug_assert!(running > 0); - running -= 1; - main_thread_worker_state = MainThreadWorkerState::LLVMing; - } - } - MainThreadWorkerState::Translating => { - bug!("trans worker should not be translating after \ - translation was already completed") - } - MainThreadWorkerState::LLVMing => { - // Already making good use of that token - } - } - } - - // Spin up what work we can, only doing this while we've got available - // parallelism slots and work left to spawn. - while work_items.len() > 0 && running < tokens.len() { - let (item, _) = work_items.pop().unwrap(); - - maybe_start_llvm_timer(cgcx.config(item.kind()), - &mut llvm_start_time); - - let cgcx = CodegenContext { - worker: get_worker_id(&mut free_worker_ids), - .. cgcx.clone() - }; - - spawn_work(cgcx, item); - running += 1; - } - - // Relinquish accidentally acquired extra tokens - tokens.truncate(running); - - let msg = coordinator_receive.recv().unwrap(); - match *msg.downcast::().ok().unwrap() { - // Save the token locally and the next turn of the loop will use - // this to spawn a new unit of work, or it may get dropped - // immediately if we have no more work to spawn. - Message::Token(token) => { - match token { - Ok(token) => { - tokens.push(token); - - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - // If the main thread token is used for LLVM work - // at the moment, we turn that thread into a regular - // LLVM worker thread, so the main thread is free - // to react to translation demand. - main_thread_worker_state = MainThreadWorkerState::Idle; - running += 1; - } - } - Err(e) => { - let msg = &format!("failed to acquire jobserver token: {}", e); - shared_emitter.fatal(msg); - // Exit the coordinator thread - panic!("{}", msg) - } - } - } - - Message::TranslationDone { llvm_work_item, cost } => { - // We keep the queue sorted by estimated processing cost, - // so that more expensive items are processed earlier. This - // is good for throughput as it gives the main thread more - // time to fill up the queue and it avoids scheduling - // expensive items to the end. - // Note, however, that this is not ideal for memory - // consumption, as LLVM module sizes are not evenly - // distributed. - let insertion_index = - work_items.binary_search_by_key(&cost, |&(_, cost)| cost); - let insertion_index = match insertion_index { - Ok(idx) | Err(idx) => idx - }; - work_items.insert(insertion_index, (llvm_work_item, cost)); - - helper.request_token(); - assert_eq!(main_thread_worker_state, - MainThreadWorkerState::Translating); - main_thread_worker_state = MainThreadWorkerState::Idle; - } - - Message::TranslationComplete => { - translation_done = true; - assert_eq!(main_thread_worker_state, - MainThreadWorkerState::Translating); - main_thread_worker_state = MainThreadWorkerState::Idle; - } - - // If a thread exits successfully then we drop a token associated - // with that worker and update our `running` count. We may later - // re-acquire a token to continue running more work. We may also not - // actually drop a token here if the worker was running with an - // "ephemeral token" - // - // Note that if the thread failed that means it panicked, so we - // abort immediately. - Message::Done { result: Ok(compiled_module), worker_id } => { - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - main_thread_worker_state = MainThreadWorkerState::Idle; - } else { - running -= 1; - } - - free_worker_ids.push(worker_id); - - match compiled_module.kind { - ModuleKind::Regular => { - compiled_modules.push(compiled_module); - } - ModuleKind::Metadata => { - assert!(compiled_metadata_module.is_none()); - compiled_metadata_module = Some(compiled_module); - } - ModuleKind::Allocator => { - assert!(compiled_allocator_module.is_none()); - compiled_allocator_module = Some(compiled_module); - } - } - } - Message::NeedsLTO { result, worker_id } => { - assert!(!started_lto); - if main_thread_worker_state == MainThreadWorkerState::LLVMing { - main_thread_worker_state = MainThreadWorkerState::Idle; - } else { - running -= 1; - } - - free_worker_ids.push(worker_id); - needs_lto.push(result); - } - Message::Done { result: Err(()), worker_id: _ } => { - shared_emitter.fatal("aborting due to worker thread failure"); - // Exit the coordinator thread - return Err(()) - } - Message::TranslateItem => { - bug!("the coordinator should not receive translation requests") - } - } - } - - if let Some(llvm_start_time) = llvm_start_time { - let total_llvm_time = Instant::now().duration_since(llvm_start_time); - // This is the top-level timing for all of LLVM, set the time-depth - // to zero. - set_time_depth(0); - print_time_passes_entry(cgcx.time_passes, - "LLVM passes", - total_llvm_time); - } - - // Regardless of what order these modules completed in, report them to - // the backend in the same order every time to ensure that we're handing - // out deterministic results. - compiled_modules.sort_by(|a, b| a.name.cmp(&b.name)); - - let compiled_metadata_module = compiled_metadata_module - .expect("Metadata module not compiled?"); - - Ok(CompiledModules { - modules: compiled_modules, - metadata_module: compiled_metadata_module, - allocator_module: compiled_allocator_module, - }) - }); - - // A heuristic that determines if we have enough LLVM WorkItems in the - // queue so that the main thread can do LLVM work instead of translation - fn queue_full_enough(items_in_queue: usize, - workers_running: usize, - max_workers: usize) -> bool { - // Tune me, plz. - items_in_queue > 0 && - items_in_queue >= max_workers.saturating_sub(workers_running / 2) - } - - fn maybe_start_llvm_timer(config: &ModuleConfig, - llvm_start_time: &mut Option) { - // We keep track of the -Ztime-passes output manually, - // since the closure-based interface does not fit well here. - if config.time_passes { - if llvm_start_time.is_none() { - *llvm_start_time = Some(Instant::now()); - } - } - } -} - -pub const TRANS_WORKER_ID: usize = ::std::usize::MAX; -pub const TRANS_WORKER_TIMELINE: time_graph::TimelineId = - time_graph::TimelineId(TRANS_WORKER_ID); -pub const TRANS_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = - time_graph::WorkPackageKind(&["#DE9597", "#FED1D3", "#FDC5C7", "#B46668", "#88494B"]); -const LLVM_WORK_PACKAGE_KIND: time_graph::WorkPackageKind = - time_graph::WorkPackageKind(&["#7DB67A", "#C6EEC4", "#ACDAAA", "#579354", "#3E6F3C"]); - -fn spawn_work(cgcx: CodegenContext, work: WorkItem) { - let depth = time_depth(); - - thread::spawn(move || { - set_time_depth(depth); - - // Set up a destructor which will fire off a message that we're done as - // we exit. - struct Bomb { - coordinator_send: Sender>, - result: Option, - worker_id: usize, - } - impl Drop for Bomb { - fn drop(&mut self) { - let worker_id = self.worker_id; - let msg = match self.result.take() { - Some(WorkItemResult::Compiled(m)) => { - Message::Done { result: Ok(m), worker_id } - } - Some(WorkItemResult::NeedsLTO(m)) => { - Message::NeedsLTO { result: m, worker_id } - } - None => Message::Done { result: Err(()), worker_id } - }; - drop(self.coordinator_send.send(Box::new(msg))); - } - } - - let mut bomb = Bomb { - coordinator_send: cgcx.coordinator_send.clone(), - result: None, - worker_id: cgcx.worker, - }; - - // Execute the work itself, and if it finishes successfully then flag - // ourselves as a success as well. - // - // Note that we ignore any `FatalError` coming out of `execute_work_item`, - // as a diagnostic was already sent off to the main thread - just - // surface that there was an error in this worker. - bomb.result = { - let timeline = cgcx.time_graph.as_ref().map(|tg| { - tg.start(time_graph::TimelineId(cgcx.worker), - LLVM_WORK_PACKAGE_KIND, - &work.name()) - }); - let mut timeline = timeline.unwrap_or(Timeline::noop()); - execute_work_item(&cgcx, work, &mut timeline).ok() - }; - }); -} - -pub fn run_assembler(cgcx: &CodegenContext, handler: &Handler, assembly: &Path, object: &Path) { - let assembler = cgcx.assembler_cmd - .as_ref() - .expect("cgcx.assembler_cmd is missing?"); - - let pname = &assembler.name; - let mut cmd = assembler.cmd.clone(); - cmd.arg("-c").arg("-o").arg(object).arg(assembly); - debug!("{:?}", cmd); - - match cmd.output() { - Ok(prog) => { - if !prog.status.success() { - let mut note = prog.stderr.clone(); - note.extend_from_slice(&prog.stdout); - - handler.struct_err(&format!("linking with `{}` failed: {}", - pname.display(), - prog.status)) - .note(&format!("{:?}", &cmd)) - .note(str::from_utf8(¬e[..]).unwrap()) - .emit(); - handler.abort_if_errors(); - } - }, - Err(e) => { - handler.err(&format!("could not exec the linker `{}`: {}", pname.display(), e)); - handler.abort_if_errors(); - } - } -} - -pub unsafe fn with_llvm_pmb(llmod: ModuleRef, - config: &ModuleConfig, - opt_level: llvm::CodeGenOptLevel, - prepare_for_thin_lto: bool, - f: &mut FnMut(llvm::PassManagerBuilderRef)) { - use std::ptr; - - // Create the PassManagerBuilder for LLVM. We configure it with - // reasonable defaults and prepare it to actually populate the pass - // manager. - let builder = llvm::LLVMPassManagerBuilderCreate(); - let opt_size = config.opt_size.unwrap_or(llvm::CodeGenOptSizeNone); - let inline_threshold = config.inline_threshold; - - let pgo_gen_path = config.pgo_gen.as_ref().map(|s| { - let s = if s.is_empty() { "default_%m.profraw" } else { s }; - CString::new(s.as_bytes()).unwrap() - }); - - let pgo_use_path = if config.pgo_use.is_empty() { - None - } else { - Some(CString::new(config.pgo_use.as_bytes()).unwrap()) - }; - - llvm::LLVMRustConfigurePassManagerBuilder( - builder, - opt_level, - config.merge_functions, - config.vectorize_slp, - config.vectorize_loop, - prepare_for_thin_lto, - pgo_gen_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), - pgo_use_path.as_ref().map_or(ptr::null(), |s| s.as_ptr()), - ); - - llvm::LLVMPassManagerBuilderSetSizeLevel(builder, opt_size as u32); - - if opt_size != llvm::CodeGenOptSizeNone { - llvm::LLVMPassManagerBuilderSetDisableUnrollLoops(builder, 1); - } - - llvm::LLVMRustAddBuilderLibraryInfo(builder, llmod, config.no_builtins); - - // Here we match what clang does (kinda). For O0 we only inline - // always-inline functions (but don't add lifetime intrinsics), at O1 we - // inline with lifetime intrinsics, and O2+ we add an inliner with a - // thresholds copied from clang. - match (opt_level, opt_size, inline_threshold) { - (.., Some(t)) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, t as u32); - } - (llvm::CodeGenOptLevel::Aggressive, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 275); - } - (_, llvm::CodeGenOptSizeDefault, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 75); - } - (_, llvm::CodeGenOptSizeAggressive, _) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 25); - } - (llvm::CodeGenOptLevel::None, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, false); - } - (llvm::CodeGenOptLevel::Less, ..) => { - llvm::LLVMRustAddAlwaysInlinePass(builder, true); - } - (llvm::CodeGenOptLevel::Default, ..) => { - llvm::LLVMPassManagerBuilderUseInlinerWithThreshold(builder, 225); - } - (llvm::CodeGenOptLevel::Other, ..) => { - bug!("CodeGenOptLevel::Other selected") - } - } - - f(builder); - llvm::LLVMPassManagerBuilderDispose(builder); -} - - -enum SharedEmitterMessage { - Diagnostic(Diagnostic), - InlineAsmError(u32, String), - AbortIfErrors, - Fatal(String), -} - -#[derive(Clone)] -pub struct SharedEmitter { - sender: Sender, -} - -pub struct SharedEmitterMain { - receiver: Receiver, -} - -impl SharedEmitter { - pub fn new() -> (SharedEmitter, SharedEmitterMain) { - let (sender, receiver) = channel(); - - (SharedEmitter { sender }, SharedEmitterMain { receiver }) - } - - fn inline_asm_error(&self, cookie: u32, msg: String) { - drop(self.sender.send(SharedEmitterMessage::InlineAsmError(cookie, msg))); - } - - fn fatal(&self, msg: &str) { - drop(self.sender.send(SharedEmitterMessage::Fatal(msg.to_string()))); - } -} - -impl Emitter for SharedEmitter { - fn emit(&mut self, db: &DiagnosticBuilder) { - drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: db.message(), - code: db.code.clone(), - lvl: db.level, - }))); - for child in &db.children { - drop(self.sender.send(SharedEmitterMessage::Diagnostic(Diagnostic { - msg: child.message(), - code: None, - lvl: child.level, - }))); - } - drop(self.sender.send(SharedEmitterMessage::AbortIfErrors)); - } -} - -impl SharedEmitterMain { - pub fn check(&self, sess: &Session, blocking: bool) { - loop { - let message = if blocking { - match self.receiver.recv() { - Ok(message) => Ok(message), - Err(_) => Err(()), - } - } else { - match self.receiver.try_recv() { - Ok(message) => Ok(message), - Err(_) => Err(()), - } - }; - - match message { - Ok(SharedEmitterMessage::Diagnostic(diag)) => { - let handler = sess.diagnostic(); - match diag.code { - Some(ref code) => { - handler.emit_with_code(&MultiSpan::new(), - &diag.msg, - code.clone(), - diag.lvl); - } - None => { - handler.emit(&MultiSpan::new(), - &diag.msg, - diag.lvl); - } - } - } - Ok(SharedEmitterMessage::InlineAsmError(cookie, msg)) => { - match Mark::from_u32(cookie).expn_info() { - Some(ei) => sess.span_err(ei.call_site, &msg), - None => sess.err(&msg), - } - } - Ok(SharedEmitterMessage::AbortIfErrors) => { - sess.abort_if_errors(); - } - Ok(SharedEmitterMessage::Fatal(msg)) => { - sess.fatal(&msg); - } - Err(_) => { - break; - } - } - - } - } -} - -pub struct OngoingCrateTranslation { - crate_name: Symbol, - link: LinkMeta, - metadata: EncodedMetadata, - windows_subsystem: Option, - linker_info: LinkerInfo, - crate_info: CrateInfo, - time_graph: Option, - coordinator_send: Sender>, - trans_worker_receive: Receiver, - shared_emitter_main: SharedEmitterMain, - future: thread::JoinHandle>, - output_filenames: Arc, -} - -impl OngoingCrateTranslation { - pub(crate) fn join( - self, - sess: &Session - ) -> (CrateTranslation, FxHashMap) { - self.shared_emitter_main.check(sess, true); - let compiled_modules = match self.future.join() { - Ok(Ok(compiled_modules)) => compiled_modules, - Ok(Err(())) => { - sess.abort_if_errors(); - panic!("expected abort due to worker thread errors") - }, - Err(_) => { - sess.fatal("Error during translation/LLVM phase."); - } - }; - - sess.abort_if_errors(); - - if let Some(time_graph) = self.time_graph { - time_graph.dump(&format!("{}-timings", self.crate_name)); - } - - let work_products = copy_all_cgu_workproducts_to_incr_comp_cache_dir(sess, - &compiled_modules); - - produce_final_output_artifacts(sess, - &compiled_modules, - &self.output_filenames); - - // FIXME: time_llvm_passes support - does this use a global context or - // something? - if sess.codegen_units() == 1 && sess.time_llvm_passes() { - unsafe { llvm::LLVMRustPrintPassTimings(); } - } - - let trans = CrateTranslation { - crate_name: self.crate_name, - link: self.link, - metadata: self.metadata, - windows_subsystem: self.windows_subsystem, - linker_info: self.linker_info, - crate_info: self.crate_info, - - modules: compiled_modules.modules, - allocator_module: compiled_modules.allocator_module, - metadata_module: compiled_modules.metadata_module, - }; - - (trans, work_products) - } - - pub(crate) fn submit_pre_translated_module_to_llvm(&self, - tcx: TyCtxt, - mtrans: ModuleTranslation) { - self.wait_for_signal_to_translate_item(); - self.check_for_errors(tcx.sess); - - // These are generally cheap and won't through off scheduling. - let cost = 0; - submit_translated_module_to_llvm(tcx, mtrans, cost); - } - - pub fn translation_finished(&self, tcx: TyCtxt) { - self.wait_for_signal_to_translate_item(); - self.check_for_errors(tcx.sess); - drop(self.coordinator_send.send(Box::new(Message::TranslationComplete))); - } - - pub fn check_for_errors(&self, sess: &Session) { - self.shared_emitter_main.check(sess, false); - } - - pub fn wait_for_signal_to_translate_item(&self) { - match self.trans_worker_receive.recv() { - Ok(Message::TranslateItem) => { - // Nothing to do - } - Ok(_) => panic!("unexpected message"), - Err(_) => { - // One of the LLVM threads must have panicked, fall through so - // error handling can be reached. - } - } - } -} - -pub(crate) fn submit_translated_module_to_llvm(tcx: TyCtxt, - mtrans: ModuleTranslation, - cost: u64) { - let llvm_work_item = WorkItem::Optimize(mtrans); - drop(tcx.tx_to_llvm_workers.lock().send(Box::new(Message::TranslationDone { - llvm_work_item, - cost, - }))); -} - -fn msvc_imps_needed(tcx: TyCtxt) -> bool { - tcx.sess.target.target.options.is_like_msvc && - tcx.sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) -} - -// Create a `__imp_ = &symbol` global for every public static `symbol`. -// This is required to satisfy `dllimport` references to static data in .rlibs -// when using MSVC linker. We do this only for data, as linker can fix up -// code references on its own. -// See #26591, #27438 -fn create_msvc_imps(cgcx: &CodegenContext, llcx: ContextRef, llmod: ModuleRef) { - if !cgcx.msvc_imps_needed { - return - } - // The x86 ABI seems to require that leading underscores are added to symbol - // names, so we need an extra underscore on 32-bit. There's also a leading - // '\x01' here which disables LLVM's symbol mangling (e.g. no extra - // underscores added in front). - let prefix = if cgcx.target_pointer_width == "32" { - "\x01__imp__" - } else { - "\x01__imp_" - }; - unsafe { - let i8p_ty = Type::i8p_llcx(llcx); - let globals = base::iter_globals(llmod) - .filter(|&val| { - llvm::LLVMRustGetLinkage(val) == llvm::Linkage::ExternalLinkage && - llvm::LLVMIsDeclaration(val) == 0 - }) - .map(move |val| { - let name = CStr::from_ptr(llvm::LLVMGetValueName(val)); - let mut imp_name = prefix.as_bytes().to_vec(); - imp_name.extend(name.to_bytes()); - let imp_name = CString::new(imp_name).unwrap(); - (imp_name, val) - }) - .collect::>(); - for (imp_name, val) in globals { - let imp = llvm::LLVMAddGlobal(llmod, - i8p_ty.to_ref(), - imp_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(imp, consts::ptrcast(val, i8p_ty)); - llvm::LLVMRustSetLinkage(imp, llvm::Linkage::ExternalLinkage); - } - } -} diff --git a/src/librustc_trans/base.rs b/src/librustc_trans/base.rs deleted file mode 100644 index feca36fa6c2..00000000000 --- a/src/librustc_trans/base.rs +++ /dev/null @@ -1,1411 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Translate the completed AST to the LLVM IR. -//! -//! Some functions here, such as trans_block and trans_expr, return a value -- -//! the result of the translation to LLVM -- while others, such as trans_fn -//! and trans_item, are called only for the side effect of adding a -//! particular definition to the LLVM IR output we're producing. -//! -//! Hopefully useful general knowledge about trans: -//! -//! * There's no way to find out the Ty type of a ValueRef. Doing so -//! would be "trying to get the eggs out of an omelette" (credit: -//! pcwalton). You can, instead, find out its TypeRef by calling val_ty, -//! but one TypeRef corresponds to many `Ty`s; for instance, tup(int, int, -//! int) and rec(x=int, y=int, z=int) will have the same TypeRef. - -use super::ModuleLlvm; -use super::ModuleSource; -use super::ModuleTranslation; -use super::ModuleKind; - -use abi; -use back::link; -use back::write::{self, OngoingCrateTranslation, create_target_machine}; -use llvm::{ContextRef, ModuleRef, ValueRef, Vector, get_param}; -use llvm; -use metadata; -use rustc::hir::def_id::{CrateNum, DefId, LOCAL_CRATE}; -use rustc::middle::lang_items::StartFnLangItem; -use rustc::middle::weak_lang_items; -use rustc::mir::mono::{Linkage, Visibility, Stats}; -use rustc::middle::cstore::{EncodedMetadata}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; -use rustc::ty::maps::Providers; -use rustc::dep_graph::{DepNode, DepConstructor}; -use rustc::ty::subst::Kind; -use rustc::middle::cstore::{self, LinkMeta, LinkagePreference}; -use rustc::middle::exported_symbols; -use rustc::util::common::{time, print_time_passes_entry}; -use rustc::session::config::{self, NoDebugInfo}; -use rustc::session::Session; -use rustc_incremental; -use allocator; -use mir::place::PlaceRef; -use attributes; -use builder::{Builder, MemFlags}; -use callee; -use common::{C_bool, C_bytes_in_context, C_i32, C_usize}; -use rustc_mir::monomorphize::collector::{self, MonoItemCollectionMode}; -use common::{self, C_struct_in_context, C_array, val_ty}; -use consts; -use context::{self, CodegenCx}; -use debuginfo; -use declare; -use meth; -use mir; -use monomorphize::Instance; -use monomorphize::partitioning::{self, PartitioningStrategy, CodegenUnit, CodegenUnitExt}; -use rustc_trans_utils::symbol_names_test; -use time_graph; -use trans_item::{MonoItem, BaseMonoItemExt, MonoItemExt, DefPathBasedNames}; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::util::nodemap::{FxHashMap, FxHashSet, DefIdSet}; -use CrateInfo; -use rustc_data_structures::sync::Lrc; -use rustc_target::spec::TargetTriple; - -use std::any::Any; -use std::collections::BTreeMap; -use std::ffi::CString; -use std::str; -use std::sync::Arc; -use std::time::{Instant, Duration}; -use std::i32; -use std::cmp; -use std::sync::mpsc; -use syntax_pos::Span; -use syntax_pos::symbol::InternedString; -use syntax::attr; -use rustc::hir; -use syntax::ast; - -use mir::operand::OperandValue; - -pub use rustc_trans_utils::check_for_rustc_errors_attr; - -pub struct StatRecorder<'a, 'tcx: 'a> { - cx: &'a CodegenCx<'a, 'tcx>, - name: Option, - istart: usize, -} - -impl<'a, 'tcx> StatRecorder<'a, 'tcx> { - pub fn new(cx: &'a CodegenCx<'a, 'tcx>, name: String) -> StatRecorder<'a, 'tcx> { - let istart = cx.stats.borrow().n_llvm_insns; - StatRecorder { - cx, - name: Some(name), - istart, - } - } -} - -impl<'a, 'tcx> Drop for StatRecorder<'a, 'tcx> { - fn drop(&mut self) { - if self.cx.sess().trans_stats() { - let mut stats = self.cx.stats.borrow_mut(); - let iend = stats.n_llvm_insns; - stats.fn_stats.push((self.name.take().unwrap(), iend - self.istart)); - stats.n_fns += 1; - // Reset LLVM insn count to avoid compound costs. - stats.n_llvm_insns = self.istart; - } - } -} - -pub fn bin_op_to_icmp_predicate(op: hir::BinOp_, - signed: bool) - -> llvm::IntPredicate { - match op { - hir::BiEq => llvm::IntEQ, - hir::BiNe => llvm::IntNE, - hir::BiLt => if signed { llvm::IntSLT } else { llvm::IntULT }, - hir::BiLe => if signed { llvm::IntSLE } else { llvm::IntULE }, - hir::BiGt => if signed { llvm::IntSGT } else { llvm::IntUGT }, - hir::BiGe => if signed { llvm::IntSGE } else { llvm::IntUGE }, - op => { - bug!("comparison_op_to_icmp_predicate: expected comparison operator, \ - found {:?}", - op) - } - } -} - -pub fn bin_op_to_fcmp_predicate(op: hir::BinOp_) -> llvm::RealPredicate { - match op { - hir::BiEq => llvm::RealOEQ, - hir::BiNe => llvm::RealUNE, - hir::BiLt => llvm::RealOLT, - hir::BiLe => llvm::RealOLE, - hir::BiGt => llvm::RealOGT, - hir::BiGe => llvm::RealOGE, - op => { - bug!("comparison_op_to_fcmp_predicate: expected comparison operator, \ - found {:?}", - op); - } - } -} - -pub fn compare_simd_types<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - lhs: ValueRef, - rhs: ValueRef, - t: Ty<'tcx>, - ret_ty: Type, - op: hir::BinOp_ -) -> ValueRef { - let signed = match t.sty { - ty::TyFloat(_) => { - let cmp = bin_op_to_fcmp_predicate(op); - return bx.sext(bx.fcmp(cmp, lhs, rhs), ret_ty); - }, - ty::TyUint(_) => false, - ty::TyInt(_) => true, - _ => bug!("compare_simd_types: invalid SIMD type"), - }; - - let cmp = bin_op_to_icmp_predicate(op, signed); - // LLVM outputs an `< size x i1 >`, so we need to perform a sign extension - // to get the correctly sized type. This will compile to a single instruction - // once the IR is converted to assembly if the SIMD instruction is supported - // by the target architecture. - bx.sext(bx.icmp(cmp, lhs, rhs), ret_ty) -} - -/// Retrieve the information we are losing (making dynamic) in an unsizing -/// adjustment. -/// -/// The `old_info` argument is a bit funny. It is intended for use -/// in an upcast, where the new vtable for an object will be derived -/// from the old one. -pub fn unsized_info<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, - source: Ty<'tcx>, - target: Ty<'tcx>, - old_info: Option) - -> ValueRef { - let (source, target) = cx.tcx.struct_lockstep_tails(source, target); - match (&source.sty, &target.sty) { - (&ty::TyArray(_, len), &ty::TySlice(_)) => { - C_usize(cx, len.unwrap_usize(cx.tcx)) - } - (&ty::TyDynamic(..), &ty::TyDynamic(..)) => { - // For now, upcasts are limited to changes in marker - // traits, and hence never actually require an actual - // change to the vtable. - old_info.expect("unsized_info: missing old info for trait upcast") - } - (_, &ty::TyDynamic(ref data, ..)) => { - let vtable_ptr = cx.layout_of(cx.tcx.mk_mut_ptr(target)) - .field(cx, abi::FAT_PTR_EXTRA); - consts::ptrcast(meth::get_vtable(cx, source, data.principal()), - vtable_ptr.llvm_type(cx)) - } - _ => bug!("unsized_info: invalid unsizing {:?} -> {:?}", - source, - target), - } -} - -/// Coerce `src` to `dst_ty`. `src_ty` must be a thin pointer. -pub fn unsize_thin_ptr<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - src: ValueRef, - src_ty: Ty<'tcx>, - dst_ty: Ty<'tcx> -) -> (ValueRef, ValueRef) { - debug!("unsize_thin_ptr: {:?} => {:?}", src_ty, dst_ty); - match (&src_ty.sty, &dst_ty.sty) { - (&ty::TyRef(_, a, _), - &ty::TyRef(_, b, _)) | - (&ty::TyRef(_, a, _), - &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) | - (&ty::TyRawPtr(ty::TypeAndMut { ty: a, .. }), - &ty::TyRawPtr(ty::TypeAndMut { ty: b, .. })) => { - assert!(bx.cx.type_is_sized(a)); - let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - let (a, b) = (src_ty.boxed_ty(), dst_ty.boxed_ty()); - assert!(bx.cx.type_is_sized(a)); - let ptr_ty = bx.cx.layout_of(b).llvm_type(bx.cx).ptr_to(); - (bx.pointercast(src, ptr_ty), unsized_info(bx.cx, a, b, None)) - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { - assert_eq!(def_a, def_b); - - let src_layout = bx.cx.layout_of(src_ty); - let dst_layout = bx.cx.layout_of(dst_ty); - let mut result = None; - for i in 0..src_layout.fields.count() { - let src_f = src_layout.field(bx.cx, i); - assert_eq!(src_layout.fields.offset(i).bytes(), 0); - assert_eq!(dst_layout.fields.offset(i).bytes(), 0); - if src_f.is_zst() { - continue; - } - assert_eq!(src_layout.size, src_f.size); - - let dst_f = dst_layout.field(bx.cx, i); - assert_ne!(src_f.ty, dst_f.ty); - assert_eq!(result, None); - result = Some(unsize_thin_ptr(bx, src, src_f.ty, dst_f.ty)); - } - let (lldata, llextra) = result.unwrap(); - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - (bx.bitcast(lldata, dst_layout.scalar_pair_element_llvm_type(bx.cx, 0)), - bx.bitcast(llextra, dst_layout.scalar_pair_element_llvm_type(bx.cx, 1))) - } - _ => bug!("unsize_thin_ptr: called on bad types"), - } -} - -/// Coerce `src`, which is a reference to a value of type `src_ty`, -/// to a value of type `dst_ty` and store the result in `dst` -pub fn coerce_unsized_into<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - src: PlaceRef<'tcx>, - dst: PlaceRef<'tcx>) { - let src_ty = src.layout.ty; - let dst_ty = dst.layout.ty; - let coerce_ptr = || { - let (base, info) = match src.load(bx).val { - OperandValue::Pair(base, info) => { - // fat-ptr to fat-ptr unsize preserves the vtable - // i.e. &'a fmt::Debug+Send => &'a fmt::Debug - // So we need to pointercast the base to ensure - // the types match up. - let thin_ptr = dst.layout.field(bx.cx, abi::FAT_PTR_ADDR); - (bx.pointercast(base, thin_ptr.llvm_type(bx.cx)), info) - } - OperandValue::Immediate(base) => { - unsize_thin_ptr(bx, base, src_ty, dst_ty) - } - OperandValue::Ref(..) => bug!() - }; - OperandValue::Pair(base, info).store(bx, dst); - }; - match (&src_ty.sty, &dst_ty.sty) { - (&ty::TyRef(..), &ty::TyRef(..)) | - (&ty::TyRef(..), &ty::TyRawPtr(..)) | - (&ty::TyRawPtr(..), &ty::TyRawPtr(..)) => { - coerce_ptr() - } - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) if def_a.is_box() && def_b.is_box() => { - coerce_ptr() - } - - (&ty::TyAdt(def_a, _), &ty::TyAdt(def_b, _)) => { - assert_eq!(def_a, def_b); - - for i in 0..def_a.variants[0].fields.len() { - let src_f = src.project_field(bx, i); - let dst_f = dst.project_field(bx, i); - - if dst_f.layout.is_zst() { - continue; - } - - if src_f.layout.ty == dst_f.layout.ty { - memcpy_ty(bx, dst_f.llval, src_f.llval, src_f.layout, - src_f.align.min(dst_f.align), MemFlags::empty()); - } else { - coerce_unsized_into(bx, src_f, dst_f); - } - } - } - _ => bug!("coerce_unsized_into: invalid coercion {:?} -> {:?}", - src_ty, - dst_ty), - } -} - -pub fn cast_shift_expr_rhs( - cx: &Builder, op: hir::BinOp_, lhs: ValueRef, rhs: ValueRef -) -> ValueRef { - cast_shift_rhs(op, lhs, rhs, |a, b| cx.trunc(a, b), |a, b| cx.zext(a, b)) -} - -fn cast_shift_rhs(op: hir::BinOp_, - lhs: ValueRef, - rhs: ValueRef, - trunc: F, - zext: G) - -> ValueRef - where F: FnOnce(ValueRef, Type) -> ValueRef, - G: FnOnce(ValueRef, Type) -> ValueRef -{ - // Shifts may have any size int on the rhs - if op.is_shift() { - let mut rhs_llty = val_ty(rhs); - let mut lhs_llty = val_ty(lhs); - if rhs_llty.kind() == Vector { - rhs_llty = rhs_llty.element_type() - } - if lhs_llty.kind() == Vector { - lhs_llty = lhs_llty.element_type() - } - let rhs_sz = rhs_llty.int_width(); - let lhs_sz = lhs_llty.int_width(); - if lhs_sz < rhs_sz { - trunc(rhs, lhs_llty) - } else if lhs_sz > rhs_sz { - // FIXME (#1877: If shifting by negative - // values becomes not undefined then this is wrong. - zext(rhs, lhs_llty) - } else { - rhs - } - } else { - rhs - } -} - -/// Returns whether this session's target will use SEH-based unwinding. -/// -/// This is only true for MSVC targets, and even then the 64-bit MSVC target -/// currently uses SEH-ish unwinding with DWARF info tables to the side (same as -/// 64-bit MinGW) instead of "full SEH". -pub fn wants_msvc_seh(sess: &Session) -> bool { - sess.target.target.options.is_like_msvc -} - -pub fn call_assume<'a, 'tcx>(bx: &Builder<'a, 'tcx>, val: ValueRef) { - let assume_intrinsic = bx.cx.get_intrinsic("llvm.assume"); - bx.call(assume_intrinsic, &[val], None); -} - -pub fn from_immediate(bx: &Builder, val: ValueRef) -> ValueRef { - if val_ty(val) == Type::i1(bx.cx) { - bx.zext(val, Type::i8(bx.cx)) - } else { - val - } -} - -pub fn to_immediate(bx: &Builder, val: ValueRef, layout: layout::TyLayout) -> ValueRef { - if let layout::Abi::Scalar(ref scalar) = layout.abi { - if scalar.is_bool() { - return bx.trunc(val, Type::i1(bx.cx)); - } - } - val -} - -pub fn call_memcpy(bx: &Builder, - dst: ValueRef, - src: ValueRef, - n_bytes: ValueRef, - align: Align, - flags: MemFlags) { - if flags.contains(MemFlags::NONTEMPORAL) { - // HACK(nox): This is inefficient but there is no nontemporal memcpy. - let val = bx.load(src, align); - let ptr = bx.pointercast(dst, val_ty(val).ptr_to()); - bx.store_with_flags(val, ptr, align, flags); - return; - } - let cx = bx.cx; - let ptr_width = &cx.sess().target.target.target_pointer_width; - let key = format!("llvm.memcpy.p0i8.p0i8.i{}", ptr_width); - let memcpy = cx.get_intrinsic(&key); - let src_ptr = bx.pointercast(src, Type::i8p(cx)); - let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); - let size = bx.intcast(n_bytes, cx.isize_ty, false); - let align = C_i32(cx, align.abi() as i32); - let volatile = C_bool(cx, flags.contains(MemFlags::VOLATILE)); - bx.call(memcpy, &[dst_ptr, src_ptr, size, align, volatile], None); -} - -pub fn memcpy_ty<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - dst: ValueRef, - src: ValueRef, - layout: TyLayout<'tcx>, - align: Align, - flags: MemFlags, -) { - let size = layout.size.bytes(); - if size == 0 { - return; - } - - call_memcpy(bx, dst, src, C_usize(bx.cx, size), align, flags); -} - -pub fn call_memset<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - ptr: ValueRef, - fill_byte: ValueRef, - size: ValueRef, - align: ValueRef, - volatile: bool) -> ValueRef { - let ptr_width = &bx.cx.sess().target.target.target_pointer_width; - let intrinsic_key = format!("llvm.memset.p0i8.i{}", ptr_width); - let llintrinsicfn = bx.cx.get_intrinsic(&intrinsic_key); - let volatile = C_bool(bx.cx, volatile); - bx.call(llintrinsicfn, &[ptr, fill_byte, size, align, volatile], None) -} - -pub fn trans_instance<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, instance: Instance<'tcx>) { - let _s = if cx.sess().trans_stats() { - let mut instance_name = String::new(); - DefPathBasedNames::new(cx.tcx, true, true) - .push_def_path(instance.def_id(), &mut instance_name); - Some(StatRecorder::new(cx, instance_name)) - } else { - None - }; - - // this is an info! to allow collecting monomorphization statistics - // and to allow finding the last function before LLVM aborts from - // release builds. - info!("trans_instance({})", instance); - - let fn_ty = instance.ty(cx.tcx); - let sig = common::ty_fn_sig(cx, fn_ty); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - - let lldecl = match cx.instances.borrow().get(&instance) { - Some(&val) => val, - None => bug!("Instance `{:?}` not already declared", instance) - }; - - cx.stats.borrow_mut().n_closures += 1; - - // The `uwtable` attribute according to LLVM is: - // - // This attribute indicates that the ABI being targeted requires that an - // unwind table entry be produced for this function even if we can show - // that no exceptions passes by it. This is normally the case for the - // ELF x86-64 abi, but it can be disabled for some compilation units. - // - // Typically when we're compiling with `-C panic=abort` (which implies this - // `no_landing_pads` check) we don't need `uwtable` because we can't - // generate any exceptions! On Windows, however, exceptions include other - // events such as illegal instructions, segfaults, etc. This means that on - // Windows we end up still needing the `uwtable` attribute even if the `-C - // panic=abort` flag is passed. - // - // You can also find more info on why Windows is whitelisted here in: - // https://bugzilla.mozilla.org/show_bug.cgi?id=1302078 - if !cx.sess().no_landing_pads() || - cx.sess().target.target.options.requires_uwtable { - attributes::emit_uwtable(lldecl, true); - } - - let mir = cx.tcx.instance_mir(instance.def); - mir::trans_mir(cx, lldecl, &mir, instance, sig); -} - -pub fn set_link_section(cx: &CodegenCx, - llval: ValueRef, - attrs: &[ast::Attribute]) { - if let Some(sect) = attr::first_attr_value_str_by_name(attrs, "link_section") { - if contains_null(§.as_str()) { - cx.sess().fatal(&format!("Illegal null byte in link_section value: `{}`", §)); - } - unsafe { - let buf = CString::new(sect.as_str().as_bytes()).unwrap(); - llvm::LLVMSetSection(llval, buf.as_ptr()); - } - } -} - -/// Create the `main` function which will initialize the rust runtime and call -/// users main function. -fn maybe_create_entry_wrapper(cx: &CodegenCx) { - let (main_def_id, span) = match *cx.sess().entry_fn.borrow() { - Some((id, span, _)) => { - (cx.tcx.hir.local_def_id(id), span) - } - None => return, - }; - - let instance = Instance::mono(cx.tcx, main_def_id); - - if !cx.codegen_unit.contains_item(&MonoItem::Fn(instance)) { - // We want to create the wrapper in the same codegen unit as Rust's main - // function. - return; - } - - let main_llfn = callee::get_fn(cx, instance); - - let et = cx.sess().entry_fn.get().map(|e| e.2); - match et { - Some(config::EntryMain) => create_entry_fn(cx, span, main_llfn, main_def_id, true), - Some(config::EntryStart) => create_entry_fn(cx, span, main_llfn, main_def_id, false), - None => {} // Do nothing. - } - - fn create_entry_fn<'cx>(cx: &'cx CodegenCx, - sp: Span, - rust_main: ValueRef, - rust_main_def_id: DefId, - use_start_lang_item: bool) { - let llfty = Type::func(&[Type::c_int(cx), Type::i8p(cx).ptr_to()], &Type::c_int(cx)); - - let main_ret_ty = cx.tcx.fn_sig(rust_main_def_id).output(); - // Given that `main()` has no arguments, - // then its return type cannot have - // late-bound regions, since late-bound - // regions must appear in the argument - // listing. - let main_ret_ty = cx.tcx.erase_regions( - &main_ret_ty.no_late_bound_regions().unwrap(), - ); - - if declare::get_defined_value(cx, "main").is_some() { - // FIXME: We should be smart and show a better diagnostic here. - cx.sess().struct_span_err(sp, "entry symbol `main` defined multiple times") - .help("did you use #[no_mangle] on `fn main`? Use #[start] instead") - .emit(); - cx.sess().abort_if_errors(); - bug!(); - } - let llfn = declare::declare_cfn(cx, "main", llfty); - - // `main` should respect same config for frame pointer elimination as rest of code - attributes::set_frame_pointer_elimination(cx, llfn); - - let bx = Builder::new_block(cx, llfn, "top"); - - debuginfo::gdb::insert_reference_to_gdb_debug_scripts_section_global(&bx); - - // Params from native main() used as args for rust start function - let param_argc = get_param(llfn, 0); - let param_argv = get_param(llfn, 1); - let arg_argc = bx.intcast(param_argc, cx.isize_ty, true); - let arg_argv = param_argv; - - let (start_fn, args) = if use_start_lang_item { - let start_def_id = cx.tcx.require_lang_item(StartFnLangItem); - let start_fn = callee::resolve_and_get_fn( - cx, - start_def_id, - cx.tcx.intern_substs(&[Kind::from(main_ret_ty)]), - ); - (start_fn, vec![bx.pointercast(rust_main, Type::i8p(cx).ptr_to()), - arg_argc, arg_argv]) - } else { - debug!("using user-defined start fn"); - (rust_main, vec![arg_argc, arg_argv]) - }; - - let result = bx.call(start_fn, &args, None); - bx.ret(bx.intcast(result, Type::c_int(cx), true)); - } -} - -fn contains_null(s: &str) -> bool { - s.bytes().any(|b| b == 0) -} - -fn write_metadata<'a, 'gcx>(tcx: TyCtxt<'a, 'gcx, 'gcx>, - llmod_id: &str, - link_meta: &LinkMeta) - -> (ContextRef, ModuleRef, EncodedMetadata) { - use std::io::Write; - use flate2::Compression; - use flate2::write::DeflateEncoder; - - let (metadata_llcx, metadata_llmod) = unsafe { - context::create_context_and_module(tcx.sess, llmod_id) - }; - - #[derive(PartialEq, Eq, PartialOrd, Ord)] - enum MetadataKind { - None, - Uncompressed, - Compressed - } - - let kind = tcx.sess.crate_types.borrow().iter().map(|ty| { - match *ty { - config::CrateTypeExecutable | - config::CrateTypeStaticlib | - config::CrateTypeCdylib => MetadataKind::None, - - config::CrateTypeRlib => MetadataKind::Uncompressed, - - config::CrateTypeDylib | - config::CrateTypeProcMacro => MetadataKind::Compressed, - } - }).max().unwrap(); - - if kind == MetadataKind::None { - return (metadata_llcx, - metadata_llmod, - EncodedMetadata::new()); - } - - let metadata = tcx.encode_metadata(link_meta); - if kind == MetadataKind::Uncompressed { - return (metadata_llcx, metadata_llmod, metadata); - } - - assert!(kind == MetadataKind::Compressed); - let mut compressed = tcx.metadata_encoding_version(); - DeflateEncoder::new(&mut compressed, Compression::fast()) - .write_all(&metadata.raw_data).unwrap(); - - let llmeta = C_bytes_in_context(metadata_llcx, &compressed); - let llconst = C_struct_in_context(metadata_llcx, &[llmeta], false); - let name = exported_symbols::metadata_symbol_name(tcx); - let buf = CString::new(name).unwrap(); - let llglobal = unsafe { - llvm::LLVMAddGlobal(metadata_llmod, val_ty(llconst).to_ref(), buf.as_ptr()) - }; - unsafe { - llvm::LLVMSetInitializer(llglobal, llconst); - let section_name = metadata::metadata_section_name(&tcx.sess.target.target); - let name = CString::new(section_name).unwrap(); - llvm::LLVMSetSection(llglobal, name.as_ptr()); - - // Also generate a .section directive to force no - // flags, at least for ELF outputs, so that the - // metadata doesn't get loaded into memory. - let directive = format!(".section {}", section_name); - let directive = CString::new(directive).unwrap(); - llvm::LLVMSetModuleInlineAsm(metadata_llmod, directive.as_ptr()) - } - return (metadata_llcx, metadata_llmod, metadata); -} - -pub struct ValueIter { - cur: ValueRef, - step: unsafe extern "C" fn(ValueRef) -> ValueRef, -} - -impl Iterator for ValueIter { - type Item = ValueRef; - - fn next(&mut self) -> Option { - let old = self.cur; - if !old.is_null() { - self.cur = unsafe { (self.step)(old) }; - Some(old) - } else { - None - } - } -} - -pub fn iter_globals(llmod: llvm::ModuleRef) -> ValueIter { - unsafe { - ValueIter { - cur: llvm::LLVMGetFirstGlobal(llmod), - step: llvm::LLVMGetNextGlobal, - } - } -} - -pub fn trans_crate<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver>) - -> OngoingCrateTranslation { - - check_for_rustc_errors_attr(tcx); - - if let Some(true) = tcx.sess.opts.debugging_opts.thinlto { - if unsafe { !llvm::LLVMRustThinLTOAvailable() } { - tcx.sess.fatal("this compiler's LLVM does not support ThinLTO"); - } - } - - if (tcx.sess.opts.debugging_opts.pgo_gen.is_some() || - !tcx.sess.opts.debugging_opts.pgo_use.is_empty()) && - unsafe { !llvm::LLVMRustPGOAvailable() } - { - tcx.sess.fatal("this compiler's LLVM does not support PGO"); - } - - let crate_hash = tcx.crate_hash(LOCAL_CRATE); - let link_meta = link::build_link_meta(crate_hash); - - // Translate the metadata. - let llmod_id = "metadata"; - let (metadata_llcx, metadata_llmod, metadata) = - time(tcx.sess, "write metadata", || { - write_metadata(tcx, llmod_id, &link_meta) - }); - - let metadata_module = ModuleTranslation { - name: link::METADATA_MODULE_NAME.to_string(), - llmod_id: llmod_id.to_string(), - source: ModuleSource::Translated(ModuleLlvm { - llcx: metadata_llcx, - llmod: metadata_llmod, - tm: create_target_machine(tcx.sess, false), - }), - kind: ModuleKind::Metadata, - }; - - let time_graph = if tcx.sess.opts.debugging_opts.trans_time_graph { - Some(time_graph::TimeGraph::new()) - } else { - None - }; - - // Skip crate items and just output metadata in -Z no-trans mode. - if tcx.sess.opts.debugging_opts.no_trans || - !tcx.sess.opts.output_types.should_trans() { - let ongoing_translation = write::start_async_translation( - tcx, - time_graph.clone(), - link_meta, - metadata, - rx, - 1); - - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); - ongoing_translation.translation_finished(tcx); - - assert_and_save_dep_graph(tcx); - - ongoing_translation.check_for_errors(tcx.sess); - - return ongoing_translation; - } - - // Run the translation item collector and partition the collected items into - // codegen units. - let codegen_units = - tcx.collect_and_partition_translation_items(LOCAL_CRATE).1; - let codegen_units = (*codegen_units).clone(); - - // Force all codegen_unit queries so they are already either red or green - // when compile_codegen_unit accesses them. We are not able to re-execute - // the codegen_unit query from just the DepNode, so an unknown color would - // lead to having to re-execute compile_codegen_unit, possibly - // unnecessarily. - if tcx.dep_graph.is_fully_enabled() { - for cgu in &codegen_units { - tcx.codegen_unit(cgu.name().clone()); - } - } - - let ongoing_translation = write::start_async_translation( - tcx, - time_graph.clone(), - link_meta, - metadata, - rx, - codegen_units.len()); - - // Translate an allocator shim, if any - let allocator_module = if let Some(kind) = *tcx.sess.allocator_kind.get() { - unsafe { - let llmod_id = "allocator"; - let (llcx, llmod) = - context::create_context_and_module(tcx.sess, llmod_id); - let modules = ModuleLlvm { - llmod, - llcx, - tm: create_target_machine(tcx.sess, false), - }; - time(tcx.sess, "write allocator module", || { - allocator::trans(tcx, &modules, kind) - }); - - Some(ModuleTranslation { - name: link::ALLOCATOR_MODULE_NAME.to_string(), - llmod_id: llmod_id.to_string(), - source: ModuleSource::Translated(modules), - kind: ModuleKind::Allocator, - }) - } - } else { - None - }; - - if let Some(allocator_module) = allocator_module { - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, allocator_module); - } - - ongoing_translation.submit_pre_translated_module_to_llvm(tcx, metadata_module); - - // We sort the codegen units by size. This way we can schedule work for LLVM - // a bit more efficiently. - let codegen_units = { - let mut codegen_units = codegen_units; - codegen_units.sort_by_cached_key(|cgu| cmp::Reverse(cgu.size_estimate())); - codegen_units - }; - - let mut total_trans_time = Duration::new(0, 0); - let mut all_stats = Stats::default(); - - for cgu in codegen_units.into_iter() { - ongoing_translation.wait_for_signal_to_translate_item(); - ongoing_translation.check_for_errors(tcx.sess); - - // First, if incremental compilation is enabled, we try to re-use the - // codegen unit from the cache. - if tcx.dep_graph.is_fully_enabled() { - let cgu_id = cgu.work_product_id(); - - // Check whether there is a previous work-product we can - // re-use. Not only must the file exist, and the inputs not - // be dirty, but the hash of the symbols we will generate must - // be the same. - if let Some(buf) = tcx.dep_graph.previous_work_product(&cgu_id) { - let dep_node = &DepNode::new(tcx, - DepConstructor::CompileCodegenUnit(cgu.name().clone())); - - // We try to mark the DepNode::CompileCodegenUnit green. If we - // succeed it means that none of the dependencies has changed - // and we can safely re-use. - if let Some(dep_node_index) = tcx.dep_graph.try_mark_green(tcx, dep_node) { - // Append ".rs" to LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}.rs", cgu.name()); - - let module = ModuleTranslation { - name: cgu.name().to_string(), - source: ModuleSource::Preexisting(buf), - kind: ModuleKind::Regular, - llmod_id, - }; - tcx.dep_graph.mark_loaded_from_cache(dep_node_index, true); - write::submit_translated_module_to_llvm(tcx, module, 0); - // Continue to next cgu, this one is done. - continue - } - } else { - // This can happen if files were deleted from the cache - // directory for some reason. We just re-compile then. - } - } - - let _timing_guard = time_graph.as_ref().map(|time_graph| { - time_graph.start(write::TRANS_WORKER_TIMELINE, - write::TRANS_WORK_PACKAGE_KIND, - &format!("codegen {}", cgu.name())) - }); - let start_time = Instant::now(); - all_stats.extend(tcx.compile_codegen_unit(*cgu.name())); - total_trans_time += start_time.elapsed(); - ongoing_translation.check_for_errors(tcx.sess); - } - - ongoing_translation.translation_finished(tcx); - - // Since the main thread is sometimes blocked during trans, we keep track - // -Ztime-passes output manually. - print_time_passes_entry(tcx.sess.time_passes(), - "translate to LLVM IR", - total_trans_time); - - if tcx.sess.opts.incremental.is_some() { - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); - } - - symbol_names_test::report_symbol_names(tcx); - - if tcx.sess.trans_stats() { - println!("--- trans stats ---"); - println!("n_glues_created: {}", all_stats.n_glues_created); - println!("n_null_glues: {}", all_stats.n_null_glues); - println!("n_real_glues: {}", all_stats.n_real_glues); - - println!("n_fns: {}", all_stats.n_fns); - println!("n_inlines: {}", all_stats.n_inlines); - println!("n_closures: {}", all_stats.n_closures); - println!("fn stats:"); - all_stats.fn_stats.sort_by_key(|&(_, insns)| insns); - for &(ref name, insns) in all_stats.fn_stats.iter() { - println!("{} insns, {}", insns, *name); - } - } - - if tcx.sess.count_llvm_insns() { - for (k, v) in all_stats.llvm_insns.iter() { - println!("{:7} {}", *v, *k); - } - } - - ongoing_translation.check_for_errors(tcx.sess); - - assert_and_save_dep_graph(tcx); - ongoing_translation -} - -fn assert_and_save_dep_graph<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - time(tcx.sess, - "assert dep graph", - || rustc_incremental::assert_dep_graph(tcx)); - - time(tcx.sess, - "serialize dep graph", - || rustc_incremental::save_dep_graph(tcx)); -} - -fn collect_and_partition_translation_items<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cnum: CrateNum, -) -> (Arc, Arc>>>) -{ - assert_eq!(cnum, LOCAL_CRATE); - - let collection_mode = match tcx.sess.opts.debugging_opts.print_trans_items { - Some(ref s) => { - let mode_string = s.to_lowercase(); - let mode_string = mode_string.trim(); - if mode_string == "eager" { - MonoItemCollectionMode::Eager - } else { - if mode_string != "lazy" { - let message = format!("Unknown codegen-item collection mode '{}'. \ - Falling back to 'lazy' mode.", - mode_string); - tcx.sess.warn(&message); - } - - MonoItemCollectionMode::Lazy - } - } - None => { - if tcx.sess.opts.cg.link_dead_code { - MonoItemCollectionMode::Eager - } else { - MonoItemCollectionMode::Lazy - } - } - }; - - let (items, inlining_map) = - time(tcx.sess, "translation item collection", || { - collector::collect_crate_mono_items(tcx, collection_mode) - }); - - tcx.sess.abort_if_errors(); - - ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, items.iter()); - - let strategy = if tcx.sess.opts.incremental.is_some() { - PartitioningStrategy::PerModule - } else { - PartitioningStrategy::FixedUnitCount(tcx.sess.codegen_units()) - }; - - let codegen_units = time(tcx.sess, "codegen unit partitioning", || { - partitioning::partition(tcx, - items.iter().cloned(), - strategy, - &inlining_map) - .into_iter() - .map(Arc::new) - .collect::>() - }); - - let translation_items: DefIdSet = items.iter().filter_map(|trans_item| { - match *trans_item { - MonoItem::Fn(ref instance) => Some(instance.def_id()), - MonoItem::Static(def_id) => Some(def_id), - _ => None, - } - }).collect(); - - if tcx.sess.opts.debugging_opts.print_trans_items.is_some() { - let mut item_to_cgus = FxHashMap(); - - for cgu in &codegen_units { - for (&trans_item, &linkage) in cgu.items() { - item_to_cgus.entry(trans_item) - .or_insert(Vec::new()) - .push((cgu.name().clone(), linkage)); - } - } - - let mut item_keys: Vec<_> = items - .iter() - .map(|i| { - let mut output = i.to_string(tcx); - output.push_str(" @@"); - let mut empty = Vec::new(); - let cgus = item_to_cgus.get_mut(i).unwrap_or(&mut empty); - cgus.as_mut_slice().sort_by_key(|&(ref name, _)| name.clone()); - cgus.dedup(); - for &(ref cgu_name, (linkage, _)) in cgus.iter() { - output.push_str(" "); - output.push_str(&cgu_name.as_str()); - - let linkage_abbrev = match linkage { - Linkage::External => "External", - Linkage::AvailableExternally => "Available", - Linkage::LinkOnceAny => "OnceAny", - Linkage::LinkOnceODR => "OnceODR", - Linkage::WeakAny => "WeakAny", - Linkage::WeakODR => "WeakODR", - Linkage::Appending => "Appending", - Linkage::Internal => "Internal", - Linkage::Private => "Private", - Linkage::ExternalWeak => "ExternalWeak", - Linkage::Common => "Common", - }; - - output.push_str("["); - output.push_str(linkage_abbrev); - output.push_str("]"); - } - output - }) - .collect(); - - item_keys.sort(); - - for item in item_keys { - println!("TRANS_ITEM {}", item); - } - } - - (Arc::new(translation_items), Arc::new(codegen_units)) -} - -impl CrateInfo { - pub fn new(tcx: TyCtxt) -> CrateInfo { - let mut info = CrateInfo { - panic_runtime: None, - compiler_builtins: None, - profiler_runtime: None, - sanitizer_runtime: None, - is_no_builtins: FxHashSet(), - native_libraries: FxHashMap(), - used_libraries: tcx.native_libraries(LOCAL_CRATE), - link_args: tcx.link_args(LOCAL_CRATE), - crate_name: FxHashMap(), - used_crates_dynamic: cstore::used_crates(tcx, LinkagePreference::RequireDynamic), - used_crates_static: cstore::used_crates(tcx, LinkagePreference::RequireStatic), - used_crate_source: FxHashMap(), - wasm_custom_sections: BTreeMap::new(), - wasm_imports: FxHashMap(), - lang_item_to_crate: FxHashMap(), - missing_lang_items: FxHashMap(), - }; - let lang_items = tcx.lang_items(); - - let load_wasm_items = tcx.sess.crate_types.borrow() - .iter() - .any(|c| *c != config::CrateTypeRlib) && - tcx.sess.opts.target_triple == TargetTriple::from_triple("wasm32-unknown-unknown"); - - if load_wasm_items { - info!("attempting to load all wasm sections"); - for &id in tcx.wasm_custom_sections(LOCAL_CRATE).iter() { - let (name, contents) = fetch_wasm_section(tcx, id); - info.wasm_custom_sections.entry(name) - .or_insert(Vec::new()) - .extend(contents); - } - info.load_wasm_imports(tcx, LOCAL_CRATE); - } - - for &cnum in tcx.crates().iter() { - info.native_libraries.insert(cnum, tcx.native_libraries(cnum)); - info.crate_name.insert(cnum, tcx.crate_name(cnum).to_string()); - info.used_crate_source.insert(cnum, tcx.used_crate_source(cnum)); - if tcx.is_panic_runtime(cnum) { - info.panic_runtime = Some(cnum); - } - if tcx.is_compiler_builtins(cnum) { - info.compiler_builtins = Some(cnum); - } - if tcx.is_profiler_runtime(cnum) { - info.profiler_runtime = Some(cnum); - } - if tcx.is_sanitizer_runtime(cnum) { - info.sanitizer_runtime = Some(cnum); - } - if tcx.is_no_builtins(cnum) { - info.is_no_builtins.insert(cnum); - } - if load_wasm_items { - for &id in tcx.wasm_custom_sections(cnum).iter() { - let (name, contents) = fetch_wasm_section(tcx, id); - info.wasm_custom_sections.entry(name) - .or_insert(Vec::new()) - .extend(contents); - } - info.load_wasm_imports(tcx, cnum); - } - let missing = tcx.missing_lang_items(cnum); - for &item in missing.iter() { - if let Ok(id) = lang_items.require(item) { - info.lang_item_to_crate.insert(item, id.krate); - } - } - - // No need to look for lang items that are whitelisted and don't - // actually need to exist. - let missing = missing.iter() - .cloned() - .filter(|&l| !weak_lang_items::whitelisted(tcx, l)) - .collect(); - info.missing_lang_items.insert(cnum, missing); - } - - return info - } - - fn load_wasm_imports(&mut self, tcx: TyCtxt, cnum: CrateNum) { - for (&id, module) in tcx.wasm_import_module_map(cnum).iter() { - let instance = Instance::mono(tcx, id); - let import_name = tcx.symbol_name(instance); - self.wasm_imports.insert(import_name.to_string(), module.clone()); - } - } -} - -fn is_translated_item(tcx: TyCtxt, id: DefId) -> bool { - let (all_trans_items, _) = - tcx.collect_and_partition_translation_items(LOCAL_CRATE); - all_trans_items.contains(&id) -} - -fn compile_codegen_unit<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - cgu: InternedString) -> Stats { - let cgu = tcx.codegen_unit(cgu); - - let start_time = Instant::now(); - let (stats, module) = module_translation(tcx, cgu); - let time_to_translate = start_time.elapsed(); - - // We assume that the cost to run LLVM on a CGU is proportional to - // the time we needed for translating it. - let cost = time_to_translate.as_secs() * 1_000_000_000 + - time_to_translate.subsec_nanos() as u64; - - write::submit_translated_module_to_llvm(tcx, - module, - cost); - return stats; - - fn module_translation<'a, 'tcx>( - tcx: TyCtxt<'a, 'tcx, 'tcx>, - cgu: Arc>) - -> (Stats, ModuleTranslation) - { - let cgu_name = cgu.name().to_string(); - - // Append ".rs" to LLVM module identifier. - // - // LLVM code generator emits a ".file filename" directive - // for ELF backends. Value of the "filename" is set as the - // LLVM module identifier. Due to a LLVM MC bug[1], LLVM - // crashes if the module identifier is same as other symbols - // such as a function name in the module. - // 1. http://llvm.org/bugs/show_bug.cgi?id=11479 - let llmod_id = format!("{}-{}.rs", - cgu.name(), - tcx.crate_disambiguator(LOCAL_CRATE) - .to_fingerprint().to_hex()); - - // Instantiate translation items without filling out definitions yet... - let cx = CodegenCx::new(tcx, cgu, &llmod_id); - let module = { - let trans_items = cx.codegen_unit - .items_in_deterministic_order(cx.tcx); - for &(trans_item, (linkage, visibility)) in &trans_items { - trans_item.predefine(&cx, linkage, visibility); - } - - // ... and now that we have everything pre-defined, fill out those definitions. - for &(trans_item, _) in &trans_items { - trans_item.define(&cx); - } - - // If this codegen unit contains the main function, also create the - // wrapper here - maybe_create_entry_wrapper(&cx); - - // Run replace-all-uses-with for statics that need it - for &(old_g, new_g) in cx.statics_to_rauw.borrow().iter() { - unsafe { - let bitcast = llvm::LLVMConstPointerCast(new_g, llvm::LLVMTypeOf(old_g)); - llvm::LLVMReplaceAllUsesWith(old_g, bitcast); - llvm::LLVMDeleteGlobal(old_g); - } - } - - // Create the llvm.used variable - // This variable has type [N x i8*] and is stored in the llvm.metadata section - if !cx.used_statics.borrow().is_empty() { - let name = CString::new("llvm.used").unwrap(); - let section = CString::new("llvm.metadata").unwrap(); - let array = C_array(Type::i8(&cx).ptr_to(), &*cx.used_statics.borrow()); - - unsafe { - let g = llvm::LLVMAddGlobal(cx.llmod, - val_ty(array).to_ref(), - name.as_ptr()); - llvm::LLVMSetInitializer(g, array); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::AppendingLinkage); - llvm::LLVMSetSection(g, section.as_ptr()); - } - } - - // Finalize debuginfo - if cx.sess().opts.debuginfo != NoDebugInfo { - debuginfo::finalize(&cx); - } - - let llvm_module = ModuleLlvm { - llcx: cx.llcx, - llmod: cx.llmod, - tm: create_target_machine(cx.sess(), false), - }; - - ModuleTranslation { - name: cgu_name, - source: ModuleSource::Translated(llvm_module), - kind: ModuleKind::Regular, - llmod_id, - } - }; - - (cx.into_stats(), module) - } -} - -pub fn provide(providers: &mut Providers) { - providers.collect_and_partition_translation_items = - collect_and_partition_translation_items; - - providers.is_translated_item = is_translated_item; - - providers.codegen_unit = |tcx, name| { - let (_, all) = tcx.collect_and_partition_translation_items(LOCAL_CRATE); - all.iter() - .find(|cgu| *cgu.name() == name) - .cloned() - .expect(&format!("failed to find cgu with name {:?}", name)) - }; - providers.compile_codegen_unit = compile_codegen_unit; - - provide_extern(providers); -} - -pub fn provide_extern(providers: &mut Providers) { - providers.dllimport_foreign_items = |tcx, krate| { - let module_map = tcx.foreign_modules(krate); - let module_map = module_map.iter() - .map(|lib| (lib.def_id, lib)) - .collect::>(); - - let dllimports = tcx.native_libraries(krate) - .iter() - .filter(|lib| { - if lib.kind != cstore::NativeLibraryKind::NativeUnknown { - return false - } - let cfg = match lib.cfg { - Some(ref cfg) => cfg, - None => return true, - }; - attr::cfg_matches(cfg, &tcx.sess.parse_sess, None) - }) - .filter_map(|lib| lib.foreign_module) - .map(|id| &module_map[&id]) - .flat_map(|module| module.foreign_items.iter().cloned()) - .collect(); - Lrc::new(dllimports) - }; - - providers.is_dllimport_foreign_item = |tcx, def_id| { - tcx.dllimport_foreign_items(def_id.krate).contains(&def_id) - }; -} - -pub fn linkage_to_llvm(linkage: Linkage) -> llvm::Linkage { - match linkage { - Linkage::External => llvm::Linkage::ExternalLinkage, - Linkage::AvailableExternally => llvm::Linkage::AvailableExternallyLinkage, - Linkage::LinkOnceAny => llvm::Linkage::LinkOnceAnyLinkage, - Linkage::LinkOnceODR => llvm::Linkage::LinkOnceODRLinkage, - Linkage::WeakAny => llvm::Linkage::WeakAnyLinkage, - Linkage::WeakODR => llvm::Linkage::WeakODRLinkage, - Linkage::Appending => llvm::Linkage::AppendingLinkage, - Linkage::Internal => llvm::Linkage::InternalLinkage, - Linkage::Private => llvm::Linkage::PrivateLinkage, - Linkage::ExternalWeak => llvm::Linkage::ExternalWeakLinkage, - Linkage::Common => llvm::Linkage::CommonLinkage, - } -} - -pub fn visibility_to_llvm(linkage: Visibility) -> llvm::Visibility { - match linkage { - Visibility::Default => llvm::Visibility::Default, - Visibility::Hidden => llvm::Visibility::Hidden, - Visibility::Protected => llvm::Visibility::Protected, - } -} - -// FIXME(mw): Anything that is produced via DepGraph::with_task() must implement -// the HashStable trait. Normally DepGraph::with_task() calls are -// hidden behind queries, but CGU creation is a special case in two -// ways: (1) it's not a query and (2) CGU are output nodes, so their -// Fingerprints are not actually needed. It remains to be clarified -// how exactly this case will be handled in the red/green system but -// for now we content ourselves with providing a no-op HashStable -// implementation for CGUs. -mod temp_stable_hash_impls { - use rustc_data_structures::stable_hasher::{StableHasherResult, StableHasher, - HashStable}; - use ModuleTranslation; - - impl HashStable for ModuleTranslation { - fn hash_stable(&self, - _: &mut HCX, - _: &mut StableHasher) { - // do nothing - } - } -} - -fn fetch_wasm_section(tcx: TyCtxt, id: DefId) -> (String, Vec) { - use rustc::mir::interpret::GlobalId; - use rustc::middle::const_val::ConstVal; - - info!("loading wasm section {:?}", id); - - let section = tcx.get_attrs(id) - .iter() - .find(|a| a.check_name("wasm_custom_section")) - .expect("missing #[wasm_custom_section] attribute") - .value_str() - .expect("malformed #[wasm_custom_section] attribute"); - - let instance = ty::Instance::mono(tcx, id); - let cid = GlobalId { - instance, - promoted: None - }; - let param_env = ty::ParamEnv::reveal_all(); - let val = tcx.const_eval(param_env.and(cid)).unwrap(); - - let const_val = match val.val { - ConstVal::Value(val) => val, - ConstVal::Unevaluated(..) => bug!("should be evaluated"), - }; - - let alloc = tcx.const_value_to_allocation((const_val, val.ty)); - (section.to_string(), alloc.bytes.clone()) -} diff --git a/src/librustc_trans/build.rs b/src/librustc_trans/build.rs deleted file mode 100644 index 97accbb4b8f..00000000000 --- a/src/librustc_trans/build.rs +++ /dev/null @@ -1,16 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - println!("cargo:rerun-if-changed=build.rs"); - println!("cargo:rerun-if-env-changed=CFG_VERSION"); - println!("cargo:rerun-if-env-changed=CFG_PREFIX"); - println!("cargo:rerun-if-env-changed=CFG_LLVM_ROOT"); -} diff --git a/src/librustc_trans/builder.rs b/src/librustc_trans/builder.rs deleted file mode 100644 index 4153c61e526..00000000000 --- a/src/librustc_trans/builder.rs +++ /dev/null @@ -1,1425 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(dead_code)] // FFI wrappers - -use llvm; -use llvm::{AtomicRmwBinOp, AtomicOrdering, SynchronizationScope, AsmDialect}; -use llvm::{Opcode, IntPredicate, RealPredicate, False, OperandBundleDef}; -use llvm::{ValueRef, BasicBlockRef, BuilderRef, ModuleRef}; -use common::*; -use type_::Type; -use value::Value; -use libc::{c_uint, c_char}; -use rustc::ty::TyCtxt; -use rustc::ty::layout::{Align, Size}; -use rustc::session::{config, Session}; - -use std::borrow::Cow; -use std::ffi::CString; -use std::ops::Range; -use std::ptr; -use syntax_pos::Span; - -// All Builders must have an llfn associated with them -#[must_use] -pub struct Builder<'a, 'tcx: 'a> { - pub llbuilder: BuilderRef, - pub cx: &'a CodegenCx<'a, 'tcx>, -} - -impl<'a, 'tcx> Drop for Builder<'a, 'tcx> { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeBuilder(self.llbuilder); - } - } -} - -// This is a really awful way to get a zero-length c-string, but better (and a -// lot more efficient) than doing str::as_c_str("", ...) every time. -fn noname() -> *const c_char { - static CNULL: c_char = 0; - &CNULL -} - -bitflags! { - pub struct MemFlags: u8 { - const VOLATILE = 1 << 0; - const NONTEMPORAL = 1 << 1; - } -} - -impl<'a, 'tcx> Builder<'a, 'tcx> { - pub fn new_block<'b>(cx: &'a CodegenCx<'a, 'tcx>, llfn: ValueRef, name: &'b str) -> Self { - let bx = Builder::with_cx(cx); - let llbb = unsafe { - let name = CString::new(name).unwrap(); - llvm::LLVMAppendBasicBlockInContext( - cx.llcx, - llfn, - name.as_ptr() - ) - }; - bx.position_at_end(llbb); - bx - } - - pub fn with_cx(cx: &'a CodegenCx<'a, 'tcx>) -> Self { - // Create a fresh builder from the crate context. - let llbuilder = unsafe { - llvm::LLVMCreateBuilderInContext(cx.llcx) - }; - Builder { - llbuilder, - cx, - } - } - - pub fn build_sibling_block<'b>(&self, name: &'b str) -> Builder<'a, 'tcx> { - Builder::new_block(self.cx, self.llfn(), name) - } - - pub fn sess(&self) -> &Session { - self.cx.sess() - } - - pub fn tcx(&self) -> TyCtxt<'a, 'tcx, 'tcx> { - self.cx.tcx - } - - pub fn llfn(&self) -> ValueRef { - unsafe { - llvm::LLVMGetBasicBlockParent(self.llbb()) - } - } - - pub fn llbb(&self) -> BasicBlockRef { - unsafe { - llvm::LLVMGetInsertBlock(self.llbuilder) - } - } - - fn count_insn(&self, category: &str) { - if self.cx.sess().trans_stats() { - self.cx.stats.borrow_mut().n_llvm_insns += 1; - } - if self.cx.sess().count_llvm_insns() { - *self.cx.stats - .borrow_mut() - .llvm_insns - .entry(category.to_string()) - .or_insert(0) += 1; - } - } - - pub fn set_value_name(&self, value: ValueRef, name: &str) { - let cname = CString::new(name.as_bytes()).unwrap(); - unsafe { - llvm::LLVMSetValueName(value, cname.as_ptr()); - } - } - - pub fn position_before(&self, insn: ValueRef) { - unsafe { - llvm::LLVMPositionBuilderBefore(self.llbuilder, insn); - } - } - - pub fn position_at_end(&self, llbb: BasicBlockRef) { - unsafe { - llvm::LLVMPositionBuilderAtEnd(self.llbuilder, llbb); - } - } - - pub fn position_at_start(&self, llbb: BasicBlockRef) { - unsafe { - llvm::LLVMRustPositionBuilderAtStart(self.llbuilder, llbb); - } - } - - pub fn ret_void(&self) { - self.count_insn("retvoid"); - unsafe { - llvm::LLVMBuildRetVoid(self.llbuilder); - } - } - - pub fn ret(&self, v: ValueRef) { - self.count_insn("ret"); - unsafe { - llvm::LLVMBuildRet(self.llbuilder, v); - } - } - - pub fn aggregate_ret(&self, ret_vals: &[ValueRef]) { - unsafe { - llvm::LLVMBuildAggregateRet(self.llbuilder, - ret_vals.as_ptr(), - ret_vals.len() as c_uint); - } - } - - pub fn br(&self, dest: BasicBlockRef) { - self.count_insn("br"); - unsafe { - llvm::LLVMBuildBr(self.llbuilder, dest); - } - } - - pub fn cond_br(&self, cond: ValueRef, then_llbb: BasicBlockRef, else_llbb: BasicBlockRef) { - self.count_insn("condbr"); - unsafe { - llvm::LLVMBuildCondBr(self.llbuilder, cond, then_llbb, else_llbb); - } - } - - pub fn switch(&self, v: ValueRef, else_llbb: BasicBlockRef, num_cases: usize) -> ValueRef { - unsafe { - llvm::LLVMBuildSwitch(self.llbuilder, v, else_llbb, num_cases as c_uint) - } - } - - pub fn indirect_br(&self, addr: ValueRef, num_dests: usize) { - self.count_insn("indirectbr"); - unsafe { - llvm::LLVMBuildIndirectBr(self.llbuilder, addr, num_dests as c_uint); - } - } - - pub fn invoke(&self, - llfn: ValueRef, - args: &[ValueRef], - then: BasicBlockRef, - catch: BasicBlockRef, - bundle: Option<&OperandBundleDef>) -> ValueRef { - self.count_insn("invoke"); - - debug!("Invoke {:?} with args ({})", - Value(llfn), - args.iter() - .map(|&v| format!("{:?}", Value(v))) - .collect::>() - .join(", ")); - - let args = self.check_call("invoke", llfn, args); - let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); - - unsafe { - llvm::LLVMRustBuildInvoke(self.llbuilder, - llfn, - args.as_ptr(), - args.len() as c_uint, - then, - catch, - bundle, - noname()) - } - } - - pub fn unreachable(&self) { - self.count_insn("unreachable"); - unsafe { - llvm::LLVMBuildUnreachable(self.llbuilder); - } - } - - /* Arithmetic */ - pub fn add(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("add"); - unsafe { - llvm::LLVMBuildAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswadd"); - unsafe { - llvm::LLVMBuildNSWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwadd"); - unsafe { - llvm::LLVMBuildNUWAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fadd(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fadd"); - unsafe { - llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fadd_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fadd"); - unsafe { - let instr = llvm::LLVMBuildFAdd(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn sub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nwsub"); - unsafe { - llvm::LLVMBuildNSWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwsub"); - unsafe { - llvm::LLVMBuildNUWSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fsub(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fsub_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sub"); - unsafe { - let instr = llvm::LLVMBuildFSub(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn mul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("mul"); - unsafe { - llvm::LLVMBuildMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nswmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nswmul"); - unsafe { - llvm::LLVMBuildNSWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn nuwmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("nuwmul"); - unsafe { - llvm::LLVMBuildNUWMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fmul(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fmul"); - unsafe { - llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fmul_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fmul"); - unsafe { - let instr = llvm::LLVMBuildFMul(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - - pub fn udiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("udiv"); - unsafe { - llvm::LLVMBuildUDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn exactudiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("exactudiv"); - unsafe { - llvm::LLVMBuildExactUDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn sdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("sdiv"); - unsafe { - llvm::LLVMBuildSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn exactsdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("exactsdiv"); - unsafe { - llvm::LLVMBuildExactSDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fdiv(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fdiv"); - unsafe { - llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn fdiv_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fdiv"); - unsafe { - let instr = llvm::LLVMBuildFDiv(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn urem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("urem"); - unsafe { - llvm::LLVMBuildURem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn srem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("srem"); - unsafe { - llvm::LLVMBuildSRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn frem(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("frem"); - unsafe { - llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn frem_fast(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("frem"); - unsafe { - let instr = llvm::LLVMBuildFRem(self.llbuilder, lhs, rhs, noname()); - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - - pub fn shl(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("shl"); - unsafe { - llvm::LLVMBuildShl(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn lshr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("lshr"); - unsafe { - llvm::LLVMBuildLShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn ashr(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ashr"); - unsafe { - llvm::LLVMBuildAShr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn and(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("and"); - unsafe { - llvm::LLVMBuildAnd(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn or(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("or"); - unsafe { - llvm::LLVMBuildOr(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn xor(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("xor"); - unsafe { - llvm::LLVMBuildXor(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn binop(&self, op: Opcode, lhs: ValueRef, rhs: ValueRef) - -> ValueRef { - self.count_insn("binop"); - unsafe { - llvm::LLVMBuildBinOp(self.llbuilder, op, lhs, rhs, noname()) - } - } - - pub fn neg(&self, v: ValueRef) -> ValueRef { - self.count_insn("neg"); - unsafe { - llvm::LLVMBuildNeg(self.llbuilder, v, noname()) - } - } - - pub fn nswneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nswneg"); - unsafe { - llvm::LLVMBuildNSWNeg(self.llbuilder, v, noname()) - } - } - - pub fn nuwneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("nuwneg"); - unsafe { - llvm::LLVMBuildNUWNeg(self.llbuilder, v, noname()) - } - } - pub fn fneg(&self, v: ValueRef) -> ValueRef { - self.count_insn("fneg"); - unsafe { - llvm::LLVMBuildFNeg(self.llbuilder, v, noname()) - } - } - - pub fn not(&self, v: ValueRef) -> ValueRef { - self.count_insn("not"); - unsafe { - llvm::LLVMBuildNot(self.llbuilder, v, noname()) - } - } - - pub fn alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { - let bx = Builder::with_cx(self.cx); - bx.position_at_start(unsafe { - llvm::LLVMGetFirstBasicBlock(self.llfn()) - }); - bx.dynamic_alloca(ty, name, align) - } - - pub fn dynamic_alloca(&self, ty: Type, name: &str, align: Align) -> ValueRef { - self.count_insn("alloca"); - unsafe { - let alloca = if name.is_empty() { - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), noname()) - } else { - let name = CString::new(name).unwrap(); - llvm::LLVMBuildAlloca(self.llbuilder, ty.to_ref(), - name.as_ptr()) - }; - llvm::LLVMSetAlignment(alloca, align.abi() as c_uint); - alloca - } - } - - pub fn free(&self, ptr: ValueRef) { - self.count_insn("free"); - unsafe { - llvm::LLVMBuildFree(self.llbuilder, ptr); - } - } - - pub fn load(&self, ptr: ValueRef, align: Align) -> ValueRef { - self.count_insn("load"); - unsafe { - let load = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); - llvm::LLVMSetAlignment(load, align.abi() as c_uint); - load - } - } - - pub fn volatile_load(&self, ptr: ValueRef) -> ValueRef { - self.count_insn("load.volatile"); - unsafe { - let insn = llvm::LLVMBuildLoad(self.llbuilder, ptr, noname()); - llvm::LLVMSetVolatile(insn, llvm::True); - insn - } - } - - pub fn atomic_load(&self, ptr: ValueRef, order: AtomicOrdering, align: Align) -> ValueRef { - self.count_insn("load.atomic"); - unsafe { - let load = llvm::LLVMRustBuildAtomicLoad(self.llbuilder, ptr, noname(), order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // However, 64-bit atomic loads on `i686-apple-darwin` appear to - // require `___atomic_load` with ABI-alignment, so it's staying. - llvm::LLVMSetAlignment(load, align.pref() as c_uint); - load - } - } - - - pub fn range_metadata(&self, load: ValueRef, range: Range) { - unsafe { - let llty = val_ty(load); - let v = [ - C_uint_big(llty, range.start), - C_uint_big(llty, range.end) - ]; - - llvm::LLVMSetMetadata(load, llvm::MD_range as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, - v.as_ptr(), - v.len() as c_uint)); - } - } - - pub fn nonnull_metadata(&self, load: ValueRef) { - unsafe { - llvm::LLVMSetMetadata(load, llvm::MD_nonnull as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); - } - } - - pub fn store(&self, val: ValueRef, ptr: ValueRef, align: Align) -> ValueRef { - self.store_with_flags(val, ptr, align, MemFlags::empty()) - } - - pub fn store_with_flags( - &self, - val: ValueRef, - ptr: ValueRef, - align: Align, - flags: MemFlags, - ) -> ValueRef { - debug!("Store {:?} -> {:?} ({:?})", Value(val), Value(ptr), flags); - assert!(!self.llbuilder.is_null()); - self.count_insn("store"); - let ptr = self.check_store(val, ptr); - unsafe { - let store = llvm::LLVMBuildStore(self.llbuilder, val, ptr); - llvm::LLVMSetAlignment(store, align.abi() as c_uint); - if flags.contains(MemFlags::VOLATILE) { - llvm::LLVMSetVolatile(store, llvm::True); - } - if flags.contains(MemFlags::NONTEMPORAL) { - // According to LLVM [1] building a nontemporal store must - // *always* point to a metadata value of the integer 1. - // - // [1]: http://llvm.org/docs/LangRef.html#store-instruction - let one = C_i32(self.cx, 1); - let node = llvm::LLVMMDNodeInContext(self.cx.llcx, &one, 1); - llvm::LLVMSetMetadata(store, llvm::MD_nontemporal as c_uint, node); - } - store - } - } - - pub fn atomic_store(&self, val: ValueRef, ptr: ValueRef, - order: AtomicOrdering, align: Align) { - debug!("Store {:?} -> {:?}", Value(val), Value(ptr)); - self.count_insn("store.atomic"); - let ptr = self.check_store(val, ptr); - unsafe { - let store = llvm::LLVMRustBuildAtomicStore(self.llbuilder, val, ptr, order); - // FIXME(eddyb) Isn't it UB to use `pref` instead of `abi` here? - // Also see `atomic_load` for more context. - llvm::LLVMSetAlignment(store, align.pref() as c_uint); - } - } - - pub fn gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("gep"); - unsafe { - llvm::LLVMBuildGEP(self.llbuilder, ptr, indices.as_ptr(), - indices.len() as c_uint, noname()) - } - } - - pub fn inbounds_gep(&self, ptr: ValueRef, indices: &[ValueRef]) -> ValueRef { - self.count_insn("inboundsgep"); - unsafe { - llvm::LLVMBuildInBoundsGEP( - self.llbuilder, ptr, indices.as_ptr(), indices.len() as c_uint, noname()) - } - } - - pub fn struct_gep(&self, ptr: ValueRef, idx: u64) -> ValueRef { - self.count_insn("structgep"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildStructGEP(self.llbuilder, ptr, idx as c_uint, noname()) - } - } - - pub fn global_string(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstring"); - unsafe { - llvm::LLVMBuildGlobalString(self.llbuilder, _str, noname()) - } - } - - pub fn global_string_ptr(&self, _str: *const c_char) -> ValueRef { - self.count_insn("globalstringptr"); - unsafe { - llvm::LLVMBuildGlobalStringPtr(self.llbuilder, _str, noname()) - } - } - - /* Casts */ - pub fn trunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("trunc"); - unsafe { - llvm::LLVMBuildTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zext"); - unsafe { - llvm::LLVMBuildZExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sext"); - unsafe { - llvm::LLVMBuildSExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptoui(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptoui"); - unsafe { - llvm::LLVMBuildFPToUI(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptosi(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptosi"); - unsafe { - llvm::LLVMBuildFPToSI(self.llbuilder, val, dest_ty.to_ref(),noname()) - } - } - - pub fn uitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("uitofp"); - unsafe { - llvm::LLVMBuildUIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sitofp(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sitofp"); - unsafe { - llvm::LLVMBuildSIToFP(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fptrunc(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fptrunc"); - unsafe { - llvm::LLVMBuildFPTrunc(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn fpext(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpext"); - unsafe { - llvm::LLVMBuildFPExt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn ptrtoint(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("ptrtoint"); - unsafe { - llvm::LLVMBuildPtrToInt(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn inttoptr(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("inttoptr"); - unsafe { - llvm::LLVMBuildIntToPtr(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("bitcast"); - unsafe { - llvm::LLVMBuildBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn zext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("zextorbitcast"); - unsafe { - llvm::LLVMBuildZExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn sext_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("sextorbitcast"); - unsafe { - llvm::LLVMBuildSExtOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn trunc_or_bitcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("truncorbitcast"); - unsafe { - llvm::LLVMBuildTruncOrBitCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn cast(&self, op: Opcode, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("cast"); - unsafe { - llvm::LLVMBuildCast(self.llbuilder, op, val, dest_ty.to_ref(), noname()) - } - } - - pub fn pointercast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("pointercast"); - unsafe { - llvm::LLVMBuildPointerCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - pub fn intcast(&self, val: ValueRef, dest_ty: Type, is_signed: bool) -> ValueRef { - self.count_insn("intcast"); - unsafe { - llvm::LLVMRustBuildIntCast(self.llbuilder, val, dest_ty.to_ref(), is_signed) - } - } - - pub fn fpcast(&self, val: ValueRef, dest_ty: Type) -> ValueRef { - self.count_insn("fpcast"); - unsafe { - llvm::LLVMBuildFPCast(self.llbuilder, val, dest_ty.to_ref(), noname()) - } - } - - - /* Comparisons */ - pub fn icmp(&self, op: IntPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("icmp"); - unsafe { - llvm::LLVMBuildICmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - pub fn fcmp(&self, op: RealPredicate, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("fcmp"); - unsafe { - llvm::LLVMBuildFCmp(self.llbuilder, op as c_uint, lhs, rhs, noname()) - } - } - - /* Miscellaneous instructions */ - pub fn empty_phi(&self, ty: Type) -> ValueRef { - self.count_insn("emptyphi"); - unsafe { - llvm::LLVMBuildPhi(self.llbuilder, ty.to_ref(), noname()) - } - } - - pub fn phi(&self, ty: Type, vals: &[ValueRef], bbs: &[BasicBlockRef]) -> ValueRef { - assert_eq!(vals.len(), bbs.len()); - let phi = self.empty_phi(ty); - self.count_insn("addincoming"); - unsafe { - llvm::LLVMAddIncoming(phi, vals.as_ptr(), - bbs.as_ptr(), - vals.len() as c_uint); - phi - } - } - - pub fn add_span_comment(&self, sp: Span, text: &str) { - if self.cx.sess().asm_comments() { - let s = format!("{} ({})", - text, - self.cx.sess().codemap().span_to_string(sp)); - debug!("{}", s); - self.add_comment(&s); - } - } - - pub fn add_comment(&self, text: &str) { - if self.cx.sess().asm_comments() { - let sanitized = text.replace("$", ""); - let comment_text = format!("{} {}", "#", - sanitized.replace("\n", "\n\t# ")); - self.count_insn("inlineasm"); - let comment_text = CString::new(comment_text).unwrap(); - let asm = unsafe { - llvm::LLVMConstInlineAsm(Type::func(&[], &Type::void(self.cx)).to_ref(), - comment_text.as_ptr(), noname(), False, - False) - }; - self.call(asm, &[], None); - } - } - - pub fn inline_asm_call(&self, asm: *const c_char, cons: *const c_char, - inputs: &[ValueRef], output: Type, - volatile: bool, alignstack: bool, - dia: AsmDialect) -> ValueRef { - self.count_insn("inlineasm"); - - let volatile = if volatile { llvm::True } - else { llvm::False }; - let alignstack = if alignstack { llvm::True } - else { llvm::False }; - - let argtys = inputs.iter().map(|v| { - debug!("Asm Input Type: {:?}", Value(*v)); - val_ty(*v) - }).collect::>(); - - debug!("Asm Output Type: {:?}", output); - let fty = Type::func(&argtys[..], &output); - unsafe { - let v = llvm::LLVMRustInlineAsm( - fty.to_ref(), asm, cons, volatile, alignstack, dia); - self.call(v, inputs, None) - } - } - - pub fn call(&self, llfn: ValueRef, args: &[ValueRef], - bundle: Option<&OperandBundleDef>) -> ValueRef { - self.count_insn("call"); - - debug!("Call {:?} with args ({})", - Value(llfn), - args.iter() - .map(|&v| format!("{:?}", Value(v))) - .collect::>() - .join(", ")); - - let args = self.check_call("call", llfn, args); - let bundle = bundle.as_ref().map(|b| b.raw()).unwrap_or(ptr::null_mut()); - - unsafe { - llvm::LLVMRustBuildCall(self.llbuilder, llfn, args.as_ptr(), - args.len() as c_uint, bundle, noname()) - } - } - - pub fn minnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("minnum"); - unsafe { - let instr = llvm::LLVMRustBuildMinNum(self.llbuilder, lhs, rhs); - if instr.is_null() { - bug!("LLVMRustBuildMinNum is not available in LLVM version < 6.0"); - } - instr - } - } - pub fn maxnum(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("maxnum"); - unsafe { - let instr = llvm::LLVMRustBuildMaxNum(self.llbuilder, lhs, rhs); - if instr.is_null() { - bug!("LLVMRustBuildMaxNum is not available in LLVM version < 6.0"); - } - instr - } - } - - pub fn select(&self, cond: ValueRef, then_val: ValueRef, else_val: ValueRef) -> ValueRef { - self.count_insn("select"); - unsafe { - llvm::LLVMBuildSelect(self.llbuilder, cond, then_val, else_val, noname()) - } - } - - pub fn va_arg(&self, list: ValueRef, ty: Type) -> ValueRef { - self.count_insn("vaarg"); - unsafe { - llvm::LLVMBuildVAArg(self.llbuilder, list, ty.to_ref(), noname()) - } - } - - pub fn extract_element(&self, vec: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("extractelement"); - unsafe { - llvm::LLVMBuildExtractElement(self.llbuilder, vec, idx, noname()) - } - } - - pub fn insert_element(&self, vec: ValueRef, elt: ValueRef, idx: ValueRef) -> ValueRef { - self.count_insn("insertelement"); - unsafe { - llvm::LLVMBuildInsertElement(self.llbuilder, vec, elt, idx, noname()) - } - } - - pub fn shuffle_vector(&self, v1: ValueRef, v2: ValueRef, mask: ValueRef) -> ValueRef { - self.count_insn("shufflevector"); - unsafe { - llvm::LLVMBuildShuffleVector(self.llbuilder, v1, v2, mask, noname()) - } - } - - pub fn vector_splat(&self, num_elts: usize, elt: ValueRef) -> ValueRef { - unsafe { - let elt_ty = val_ty(elt); - let undef = llvm::LLVMGetUndef(Type::vector(&elt_ty, num_elts as u64).to_ref()); - let vec = self.insert_element(undef, elt, C_i32(self.cx, 0)); - let vec_i32_ty = Type::vector(&Type::i32(self.cx), num_elts as u64); - self.shuffle_vector(vec, undef, C_null(vec_i32_ty)) - } - } - - pub fn vector_reduce_fadd_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fadd_fast"); - unsafe { - // FIXME: add a non-fast math version once - // https://bugs.llvm.org/show_bug.cgi?id=36732 - // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFAdd(self.llbuilder, acc, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFAdd is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_fmul_fast(&self, acc: ValueRef, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmul_fast"); - unsafe { - // FIXME: add a non-fast math version once - // https://bugs.llvm.org/show_bug.cgi?id=36732 - // is fixed. - let instr = llvm::LLVMRustBuildVectorReduceFMul(self.llbuilder, acc, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMul is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_add(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.add"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAdd(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceAdd is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_mul(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.mul"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMul(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMul is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_and(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.and"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceAnd(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceAnd is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_or(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.or"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceOr(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceOr is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_xor(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.xor"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceXor(self.llbuilder, src); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceXor is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmin(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmin"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ false); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmax(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmax"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ false); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_fmin_fast(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmin_fast"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMin(self.llbuilder, src, /*NoNaNs:*/ true); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMin is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_fmax_fast(&self, src: ValueRef) -> ValueRef { - self.count_insn("vector.reduce.fmax_fast"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceFMax(self.llbuilder, src, /*NoNaNs:*/ true); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceFMax is not available in LLVM version < 5.0"); - } - llvm::LLVMRustSetHasUnsafeAlgebra(instr); - instr - } - } - pub fn vector_reduce_min(&self, src: ValueRef, is_signed: bool) -> ValueRef { - self.count_insn("vector.reduce.min"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMin(self.llbuilder, src, is_signed); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMin is not available in LLVM version < 5.0"); - } - instr - } - } - pub fn vector_reduce_max(&self, src: ValueRef, is_signed: bool) -> ValueRef { - self.count_insn("vector.reduce.max"); - unsafe { - let instr = llvm::LLVMRustBuildVectorReduceMax(self.llbuilder, src, is_signed); - if instr.is_null() { - bug!("LLVMRustBuildVectorReduceMax is not available in LLVM version < 5.0"); - } - instr - } - } - - pub fn extract_value(&self, agg_val: ValueRef, idx: u64) -> ValueRef { - self.count_insn("extractvalue"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildExtractValue(self.llbuilder, agg_val, idx as c_uint, noname()) - } - } - - pub fn insert_value(&self, agg_val: ValueRef, elt: ValueRef, - idx: u64) -> ValueRef { - self.count_insn("insertvalue"); - assert_eq!(idx as c_uint as u64, idx); - unsafe { - llvm::LLVMBuildInsertValue(self.llbuilder, agg_val, elt, idx as c_uint, - noname()) - } - } - - pub fn is_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnull"); - unsafe { - llvm::LLVMBuildIsNull(self.llbuilder, val, noname()) - } - } - - pub fn is_not_null(&self, val: ValueRef) -> ValueRef { - self.count_insn("isnotnull"); - unsafe { - llvm::LLVMBuildIsNotNull(self.llbuilder, val, noname()) - } - } - - pub fn ptrdiff(&self, lhs: ValueRef, rhs: ValueRef) -> ValueRef { - self.count_insn("ptrdiff"); - unsafe { - llvm::LLVMBuildPtrDiff(self.llbuilder, lhs, rhs, noname()) - } - } - - pub fn trap(&self) { - unsafe { - let bb: BasicBlockRef = llvm::LLVMGetInsertBlock(self.llbuilder); - let fn_: ValueRef = llvm::LLVMGetBasicBlockParent(bb); - let m: ModuleRef = llvm::LLVMGetGlobalParent(fn_); - let p = "llvm.trap\0".as_ptr(); - let t: ValueRef = llvm::LLVMGetNamedFunction(m, p as *const _); - assert!((t as isize != 0)); - let args: &[ValueRef] = &[]; - self.count_insn("trap"); - llvm::LLVMRustBuildCall(self.llbuilder, t, - args.as_ptr(), args.len() as c_uint, - ptr::null_mut(), - noname()); - } - } - - pub fn landing_pad(&self, ty: Type, pers_fn: ValueRef, - num_clauses: usize) -> ValueRef { - self.count_insn("landingpad"); - unsafe { - llvm::LLVMBuildLandingPad(self.llbuilder, ty.to_ref(), pers_fn, - num_clauses as c_uint, noname()) - } - } - - pub fn add_clause(&self, landing_pad: ValueRef, clause: ValueRef) { - unsafe { - llvm::LLVMAddClause(landing_pad, clause); - } - } - - pub fn set_cleanup(&self, landing_pad: ValueRef) { - self.count_insn("setcleanup"); - unsafe { - llvm::LLVMSetCleanup(landing_pad, llvm::True); - } - } - - pub fn resume(&self, exn: ValueRef) -> ValueRef { - self.count_insn("resume"); - unsafe { - llvm::LLVMBuildResume(self.llbuilder, exn) - } - } - - pub fn cleanup_pad(&self, - parent: Option, - args: &[ValueRef]) -> ValueRef { - self.count_insn("cleanuppad"); - let parent = parent.unwrap_or(ptr::null_mut()); - let name = CString::new("cleanuppad").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCleanupPad(self.llbuilder, - parent, - args.len() as c_uint, - args.as_ptr(), - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for cleanuppad"); - return ret - } - - pub fn cleanup_ret(&self, cleanup: ValueRef, - unwind: Option) -> ValueRef { - self.count_insn("cleanupret"); - let unwind = unwind.unwrap_or(ptr::null_mut()); - let ret = unsafe { - llvm::LLVMRustBuildCleanupRet(self.llbuilder, cleanup, unwind) - }; - assert!(!ret.is_null(), "LLVM does not have support for cleanupret"); - return ret - } - - pub fn catch_pad(&self, - parent: ValueRef, - args: &[ValueRef]) -> ValueRef { - self.count_insn("catchpad"); - let name = CString::new("catchpad").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCatchPad(self.llbuilder, parent, - args.len() as c_uint, args.as_ptr(), - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchpad"); - return ret - } - - pub fn catch_ret(&self, pad: ValueRef, unwind: BasicBlockRef) -> ValueRef { - self.count_insn("catchret"); - let ret = unsafe { - llvm::LLVMRustBuildCatchRet(self.llbuilder, pad, unwind) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchret"); - return ret - } - - pub fn catch_switch(&self, - parent: Option, - unwind: Option, - num_handlers: usize) -> ValueRef { - self.count_insn("catchswitch"); - let parent = parent.unwrap_or(ptr::null_mut()); - let unwind = unwind.unwrap_or(ptr::null_mut()); - let name = CString::new("catchswitch").unwrap(); - let ret = unsafe { - llvm::LLVMRustBuildCatchSwitch(self.llbuilder, parent, unwind, - num_handlers as c_uint, - name.as_ptr()) - }; - assert!(!ret.is_null(), "LLVM does not have support for catchswitch"); - return ret - } - - pub fn add_handler(&self, catch_switch: ValueRef, handler: BasicBlockRef) { - unsafe { - llvm::LLVMRustAddHandler(catch_switch, handler); - } - } - - pub fn set_personality_fn(&self, personality: ValueRef) { - unsafe { - llvm::LLVMSetPersonalityFn(self.llfn(), personality); - } - } - - // Atomic Operations - pub fn atomic_cmpxchg(&self, dst: ValueRef, - cmp: ValueRef, src: ValueRef, - order: AtomicOrdering, - failure_order: AtomicOrdering, - weak: llvm::Bool) -> ValueRef { - unsafe { - llvm::LLVMRustBuildAtomicCmpXchg(self.llbuilder, dst, cmp, src, - order, failure_order, weak) - } - } - pub fn atomic_rmw(&self, op: AtomicRmwBinOp, - dst: ValueRef, src: ValueRef, - order: AtomicOrdering) -> ValueRef { - unsafe { - llvm::LLVMBuildAtomicRMW(self.llbuilder, op, dst, src, order, False) - } - } - - pub fn atomic_fence(&self, order: AtomicOrdering, scope: SynchronizationScope) { - unsafe { - llvm::LLVMRustBuildAtomicFence(self.llbuilder, order, scope); - } - } - - pub fn add_case(&self, s: ValueRef, on_val: ValueRef, dest: BasicBlockRef) { - unsafe { - llvm::LLVMAddCase(s, on_val, dest) - } - } - - pub fn add_incoming_to_phi(&self, phi: ValueRef, val: ValueRef, bb: BasicBlockRef) { - unsafe { - llvm::LLVMAddIncoming(phi, &val, &bb, 1 as c_uint); - } - } - - pub fn set_invariant_load(&self, load: ValueRef) { - unsafe { - llvm::LLVMSetMetadata(load, llvm::MD_invariant_load as c_uint, - llvm::LLVMMDNodeInContext(self.cx.llcx, ptr::null(), 0)); - } - } - - /// Returns the ptr value that should be used for storing `val`. - fn check_store<'b>(&self, - val: ValueRef, - ptr: ValueRef) -> ValueRef { - let dest_ptr_ty = val_ty(ptr); - let stored_ty = val_ty(val); - let stored_ptr_ty = stored_ty.ptr_to(); - - assert_eq!(dest_ptr_ty.kind(), llvm::TypeKind::Pointer); - - if dest_ptr_ty == stored_ptr_ty { - ptr - } else { - debug!("Type mismatch in store. \ - Expected {:?}, got {:?}; inserting bitcast", - dest_ptr_ty, stored_ptr_ty); - self.bitcast(ptr, stored_ptr_ty) - } - } - - /// Returns the args that should be used for a call to `llfn`. - fn check_call<'b>(&self, - typ: &str, - llfn: ValueRef, - args: &'b [ValueRef]) -> Cow<'b, [ValueRef]> { - let mut fn_ty = val_ty(llfn); - // Strip off pointers - while fn_ty.kind() == llvm::TypeKind::Pointer { - fn_ty = fn_ty.element_type(); - } - - assert!(fn_ty.kind() == llvm::TypeKind::Function, - "builder::{} not passed a function, but {:?}", typ, fn_ty); - - let param_tys = fn_ty.func_params(); - - let all_args_match = param_tys.iter() - .zip(args.iter().map(|&v| val_ty(v))) - .all(|(expected_ty, actual_ty)| *expected_ty == actual_ty); - - if all_args_match { - return Cow::Borrowed(args); - } - - let casted_args: Vec<_> = param_tys.into_iter() - .zip(args.iter()) - .enumerate() - .map(|(i, (expected_ty, &actual_val))| { - let actual_ty = val_ty(actual_val); - if expected_ty != actual_ty { - debug!("Type mismatch in function call of {:?}. \ - Expected {:?} for param {}, got {:?}; injecting bitcast", - Value(llfn), - expected_ty, i, actual_ty); - self.bitcast(actual_val, expected_ty) - } else { - actual_val - } - }) - .collect(); - - return Cow::Owned(casted_args); - } - - pub fn lifetime_start(&self, ptr: ValueRef, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.start", ptr, size); - } - - pub fn lifetime_end(&self, ptr: ValueRef, size: Size) { - self.call_lifetime_intrinsic("llvm.lifetime.end", ptr, size); - } - - /// If LLVM lifetime intrinsic support is enabled (i.e. optimizations - /// on), and `ptr` is nonzero-sized, then extracts the size of `ptr` - /// and the intrinsic for `lt` and passes them to `emit`, which is in - /// charge of generating code to call the passed intrinsic on whatever - /// block of generated code is targeted for the intrinsic. - /// - /// If LLVM lifetime intrinsic support is disabled (i.e. optimizations - /// off) or `ptr` is zero-sized, then no-op (does not call `emit`). - fn call_lifetime_intrinsic(&self, intrinsic: &str, ptr: ValueRef, size: Size) { - if self.cx.sess().opts.optimize == config::OptLevel::No { - return; - } - - let size = size.bytes(); - if size == 0 { - return; - } - - let lifetime_intrinsic = self.cx.get_intrinsic(intrinsic); - - let ptr = self.pointercast(ptr, Type::i8p(self.cx)); - self.call(lifetime_intrinsic, &[C_u64(self.cx, size), ptr], None); - } -} diff --git a/src/librustc_trans/callee.rs b/src/librustc_trans/callee.rs deleted file mode 100644 index 9263d9a5f5d..00000000000 --- a/src/librustc_trans/callee.rs +++ /dev/null @@ -1,232 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Handles translation of callees as well as other call-related -//! things. Callees are a superset of normal rust values and sometimes -//! have different representations. In particular, top-level fn items -//! and methods are represented as just a fn ptr and not a full -//! closure. - -use attributes; -use common::{self, CodegenCx}; -use consts; -use declare; -use llvm::{self, ValueRef}; -use monomorphize::Instance; -use type_of::LayoutLlvmExt; - -use rustc::hir::def_id::DefId; -use rustc::ty::{self, TypeFoldable}; -use rustc::ty::layout::LayoutOf; -use rustc::ty::subst::Substs; -use rustc_target::spec::PanicStrategy; - -/// Translates a reference to a fn/method item, monomorphizing and -/// inlining as it goes. -/// -/// # Parameters -/// -/// - `cx`: the crate context -/// - `instance`: the instance to be instantiated -pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>) - -> ValueRef -{ - let tcx = cx.tcx; - - debug!("get_fn(instance={:?})", instance); - - assert!(!instance.substs.needs_infer()); - assert!(!instance.substs.has_escaping_regions()); - assert!(!instance.substs.has_param_types()); - - let fn_ty = instance.ty(cx.tcx); - if let Some(&llfn) = cx.instances.borrow().get(&instance) { - return llfn; - } - - let sym = tcx.symbol_name(instance).as_str(); - debug!("get_fn({:?}: {:?}) => {}", instance, fn_ty, sym); - - // Create a fn pointer with the substituted signature. - let fn_ptr_ty = tcx.mk_fn_ptr(common::ty_fn_sig(cx, fn_ty)); - let llptrty = cx.layout_of(fn_ptr_ty).llvm_type(cx); - - let llfn = if let Some(llfn) = declare::get_declared_value(cx, &sym) { - // This is subtle and surprising, but sometimes we have to bitcast - // the resulting fn pointer. The reason has to do with external - // functions. If you have two crates that both bind the same C - // library, they may not use precisely the same types: for - // example, they will probably each declare their own structs, - // which are distinct types from LLVM's point of view (nominal - // types). - // - // Now, if those two crates are linked into an application, and - // they contain inlined code, you can wind up with a situation - // where both of those functions wind up being loaded into this - // application simultaneously. In that case, the same function - // (from LLVM's point of view) requires two types. But of course - // LLVM won't allow one function to have two types. - // - // What we currently do, therefore, is declare the function with - // one of the two types (whichever happens to come first) and then - // bitcast as needed when the function is referenced to make sure - // it has the type we expect. - // - // This can occur on either a crate-local or crate-external - // reference. It also occurs when testing libcore and in some - // other weird situations. Annoying. - if common::val_ty(llfn) != llptrty { - debug!("get_fn: casting {:?} to {:?}", llfn, llptrty); - consts::ptrcast(llfn, llptrty) - } else { - debug!("get_fn: not casting pointer!"); - llfn - } - } else { - let llfn = declare::declare_fn(cx, &sym, fn_ty); - assert_eq!(common::val_ty(llfn), llptrty); - debug!("get_fn: not casting pointer!"); - - if instance.def.is_inline(tcx) { - attributes::inline(llfn, attributes::InlineAttr::Hint); - } - attributes::from_fn_attrs(cx, llfn, instance.def.def_id()); - - let instance_def_id = instance.def_id(); - - // Perhaps questionable, but we assume that anything defined - // *in Rust code* may unwind. Foreign items like `extern "C" { - // fn foo(); }` are assumed not to unwind **unless** they have - // a `#[unwind]` attribute. - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { - if !tcx.is_foreign_item(instance_def_id) { - attributes::unwind(llfn, true); - } - } - - // Apply an appropriate linkage/visibility value to our item that we - // just declared. - // - // This is sort of subtle. Inside our codegen unit we started off - // compilation by predefining all our own `TransItem` instances. That - // is, everything we're translating ourselves is already defined. That - // means that anything we're actually translating in this codegen unit - // will have hit the above branch in `get_declared_value`. As a result, - // we're guaranteed here that we're declaring a symbol that won't get - // defined, or in other words we're referencing a value from another - // codegen unit or even another crate. - // - // So because this is a foreign value we blanket apply an external - // linkage directive because it's coming from a different object file. - // The visibility here is where it gets tricky. This symbol could be - // referencing some foreign crate or foreign library (an `extern` - // block) in which case we want to leave the default visibility. We may - // also, though, have multiple codegen units. It could be a - // monomorphization, in which case its expected visibility depends on - // whether we are sharing generics or not. The important thing here is - // that the visibility we apply to the declaration is the same one that - // has been applied to the definition (wherever that definition may be). - unsafe { - llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::ExternalLinkage); - - let is_generic = instance.substs.types().next().is_some(); - - if is_generic { - // This is a monomorphization. Its expected visibility depends - // on whether we are in share-generics mode. - - if cx.tcx.share_generics() { - // We are in share_generics mode. - - if instance_def_id.is_local() { - // This is a definition from the current crate. If the - // definition is unreachable for downstream crates or - // the current crate does not re-export generics, the - // definition of the instance will have been declared - // as `hidden`. - if cx.tcx.is_unreachable_local_definition(instance_def_id) || - !cx.tcx.local_crate_exports_generics() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a monomorphization of a generic function - // defined in an upstream crate. - if cx.tcx.upstream_monomorphizations_for(instance_def_id) - .map(|set| set.contains_key(instance.substs)) - .unwrap_or(false) { - // This is instantiated in another crate. It cannot - // be `hidden`. - } else { - // This is a local instantiation of an upstream definition. - // If the current crate does not re-export it - // (because it is a C library or an executable), it - // will have been declared `hidden`. - if !cx.tcx.local_crate_exports_generics() { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } - } - } else { - // When not sharing generics, all instances are in the same - // crate and have hidden visibility - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a non-generic function - if cx.tcx.is_translated_item(instance_def_id) { - // This is a function that is instantiated in the local crate - - if instance_def_id.is_local() { - // This is function that is defined in the local crate. - // If it is not reachable, it is hidden. - if !cx.tcx.is_reachable_non_generic(instance_def_id) { - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } else { - // This is a function from an upstream crate that has - // been instantiated here. These are always hidden. - llvm::LLVMRustSetVisibility(llfn, llvm::Visibility::Hidden); - } - } - } - } - - if cx.use_dll_storage_attrs && - tcx.is_dllimport_foreign_item(instance_def_id) - { - unsafe { - llvm::LLVMSetDLLStorageClass(llfn, llvm::DLLStorageClass::DllImport); - } - } - - llfn - }; - - cx.instances.borrow_mut().insert(instance, llfn); - - llfn -} - -pub fn resolve_and_get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - substs: &'tcx Substs<'tcx>) - -> ValueRef -{ - get_fn( - cx, - ty::Instance::resolve( - cx.tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs - ).unwrap() - ) -} diff --git a/src/librustc_trans/common.rs b/src/librustc_trans/common.rs deleted file mode 100644 index 75b56be3c16..00000000000 --- a/src/librustc_trans/common.rs +++ /dev/null @@ -1,449 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_camel_case_types, non_snake_case)] - -//! Code that is useful in various trans modules. - -use llvm; -use llvm::{ValueRef, ContextRef, TypeKind}; -use llvm::{True, False, Bool, OperandBundleDef}; -use rustc::hir::def_id::DefId; -use rustc::middle::lang_items::LangItem; -use abi; -use base; -use builder::Builder; -use consts; -use declare; -use type_::Type; -use type_of::LayoutLlvmExt; -use value::Value; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::ty::layout::{HasDataLayout, LayoutOf}; -use rustc::hir; - -use libc::{c_uint, c_char}; -use std::iter; - -use rustc_target::spec::abi::Abi; -use syntax::symbol::LocalInternedString; -use syntax_pos::{Span, DUMMY_SP}; - -pub use context::CodegenCx; - -pub fn type_needs_drop<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.needs_drop(tcx, ty::ParamEnv::reveal_all()) -} - -pub fn type_is_sized<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_sized(tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) -} - -pub fn type_is_freeze<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, ty: Ty<'tcx>) -> bool { - ty.is_freeze(tcx, ty::ParamEnv::reveal_all(), DUMMY_SP) -} - -/* -* A note on nomenclature of linking: "extern", "foreign", and "upcall". -* -* An "extern" is an LLVM symbol we wind up emitting an undefined external -* reference to. This means "we don't have the thing in this compilation unit, -* please make sure you link it in at runtime". This could be a reference to -* C code found in a C library, or rust code found in a rust crate. -* -* Most "externs" are implicitly declared (automatically) as a result of a -* user declaring an extern _module_ dependency; this causes the rust driver -* to locate an extern crate, scan its compilation metadata, and emit extern -* declarations for any symbols used by the declaring crate. -* -* A "foreign" is an extern that references C (or other non-rust ABI) code. -* There is no metadata to scan for extern references so in these cases either -* a header-digester like bindgen, or manual function prototypes, have to -* serve as declarators. So these are usually given explicitly as prototype -* declarations, in rust code, with ABI attributes on them noting which ABI to -* link via. -* -* An "upcall" is a foreign call generated by the compiler (not corresponding -* to any user-written call in the code) into the runtime library, to perform -* some helper task such as bringing a task to life, allocating memory, etc. -* -*/ - -/// A structure representing an active landing pad for the duration of a basic -/// block. -/// -/// Each `Block` may contain an instance of this, indicating whether the block -/// is part of a landing pad or not. This is used to make decision about whether -/// to emit `invoke` instructions (e.g. in a landing pad we don't continue to -/// use `invoke`) and also about various function call metadata. -/// -/// For GNU exceptions (`landingpad` + `resume` instructions) this structure is -/// just a bunch of `None` instances (not too interesting), but for MSVC -/// exceptions (`cleanuppad` + `cleanupret` instructions) this contains data. -/// When inside of a landing pad, each function call in LLVM IR needs to be -/// annotated with which landing pad it's a part of. This is accomplished via -/// the `OperandBundleDef` value created for MSVC landing pads. -pub struct Funclet { - cleanuppad: ValueRef, - operand: OperandBundleDef, -} - -impl Funclet { - pub fn new(cleanuppad: ValueRef) -> Funclet { - Funclet { - cleanuppad, - operand: OperandBundleDef::new("funclet", &[cleanuppad]), - } - } - - pub fn cleanuppad(&self) -> ValueRef { - self.cleanuppad - } - - pub fn bundle(&self) -> &OperandBundleDef { - &self.operand - } -} - -pub fn val_ty(v: ValueRef) -> Type { - unsafe { - Type::from_ref(llvm::LLVMTypeOf(v)) - } -} - -// LLVM constant constructors. -pub fn C_null(t: Type) -> ValueRef { - unsafe { - llvm::LLVMConstNull(t.to_ref()) - } -} - -pub fn C_undef(t: Type) -> ValueRef { - unsafe { - llvm::LLVMGetUndef(t.to_ref()) - } -} - -pub fn C_int(t: Type, i: i64) -> ValueRef { - unsafe { - llvm::LLVMConstInt(t.to_ref(), i as u64, True) - } -} - -pub fn C_uint(t: Type, i: u64) -> ValueRef { - unsafe { - llvm::LLVMConstInt(t.to_ref(), i, False) - } -} - -pub fn C_uint_big(t: Type, u: u128) -> ValueRef { - unsafe { - let words = [u as u64, (u >> 64) as u64]; - llvm::LLVMConstIntOfArbitraryPrecision(t.to_ref(), 2, words.as_ptr()) - } -} - -pub fn C_bool(cx: &CodegenCx, val: bool) -> ValueRef { - C_uint(Type::i1(cx), val as u64) -} - -pub fn C_i32(cx: &CodegenCx, i: i32) -> ValueRef { - C_int(Type::i32(cx), i as i64) -} - -pub fn C_u32(cx: &CodegenCx, i: u32) -> ValueRef { - C_uint(Type::i32(cx), i as u64) -} - -pub fn C_u64(cx: &CodegenCx, i: u64) -> ValueRef { - C_uint(Type::i64(cx), i) -} - -pub fn C_usize(cx: &CodegenCx, i: u64) -> ValueRef { - let bit_size = cx.data_layout().pointer_size.bits(); - if bit_size < 64 { - // make sure it doesn't overflow - assert!(i < (1< ValueRef { - C_uint(Type::i8(cx), i as u64) -} - - -// This is a 'c-like' raw string, which differs from -// our boxed-and-length-annotated strings. -pub fn C_cstr(cx: &CodegenCx, s: LocalInternedString, null_terminated: bool) -> ValueRef { - unsafe { - if let Some(&llval) = cx.const_cstr_cache.borrow().get(&s) { - return llval; - } - - let sc = llvm::LLVMConstStringInContext(cx.llcx, - s.as_ptr() as *const c_char, - s.len() as c_uint, - !null_terminated as Bool); - let sym = cx.generate_local_symbol_name("str"); - let g = declare::define_global(cx, &sym[..], val_ty(sc)).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", sym); - }); - llvm::LLVMSetInitializer(g, sc); - llvm::LLVMSetGlobalConstant(g, True); - llvm::LLVMRustSetLinkage(g, llvm::Linkage::InternalLinkage); - - cx.const_cstr_cache.borrow_mut().insert(s, g); - g - } -} - -// NB: Do not use `do_spill_noroot` to make this into a constant string, or -// you will be kicked off fast isel. See issue #4352 for an example of this. -pub fn C_str_slice(cx: &CodegenCx, s: LocalInternedString) -> ValueRef { - let len = s.len(); - let cs = consts::ptrcast(C_cstr(cx, s, false), - cx.layout_of(cx.tcx.mk_str()).llvm_type(cx).ptr_to()); - C_fat_ptr(cx, cs, C_usize(cx, len as u64)) -} - -pub fn C_fat_ptr(cx: &CodegenCx, ptr: ValueRef, meta: ValueRef) -> ValueRef { - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - C_struct(cx, &[ptr, meta], false) -} - -pub fn C_struct(cx: &CodegenCx, elts: &[ValueRef], packed: bool) -> ValueRef { - C_struct_in_context(cx.llcx, elts, packed) -} - -pub fn C_struct_in_context(llcx: ContextRef, elts: &[ValueRef], packed: bool) -> ValueRef { - unsafe { - llvm::LLVMConstStructInContext(llcx, - elts.as_ptr(), elts.len() as c_uint, - packed as Bool) - } -} - -pub fn C_array(ty: Type, elts: &[ValueRef]) -> ValueRef { - unsafe { - return llvm::LLVMConstArray(ty.to_ref(), elts.as_ptr(), elts.len() as c_uint); - } -} - -pub fn C_vector(elts: &[ValueRef]) -> ValueRef { - unsafe { - return llvm::LLVMConstVector(elts.as_ptr(), elts.len() as c_uint); - } -} - -pub fn C_bytes(cx: &CodegenCx, bytes: &[u8]) -> ValueRef { - C_bytes_in_context(cx.llcx, bytes) -} - -pub fn C_bytes_in_context(llcx: ContextRef, bytes: &[u8]) -> ValueRef { - unsafe { - let ptr = bytes.as_ptr() as *const c_char; - return llvm::LLVMConstStringInContext(llcx, ptr, bytes.len() as c_uint, True); - } -} - -pub fn const_get_elt(v: ValueRef, idx: u64) -> ValueRef { - unsafe { - assert_eq!(idx as c_uint as u64, idx); - let us = &[idx as c_uint]; - let r = llvm::LLVMConstExtractValue(v, us.as_ptr(), us.len() as c_uint); - - debug!("const_get_elt(v={:?}, idx={}, r={:?})", - Value(v), idx, Value(r)); - - r - } -} - -pub fn const_get_real(v: ValueRef) -> Option<(f64, bool)> { - unsafe { - if is_const_real(v) { - let mut loses_info: llvm::Bool = ::std::mem::uninitialized(); - let r = llvm::LLVMConstRealGetDouble(v, &mut loses_info as *mut llvm::Bool); - let loses_info = if loses_info == 1 { true } else { false }; - Some((r, loses_info)) - } else { - None - } - } -} - -pub fn const_to_uint(v: ValueRef) -> u64 { - unsafe { - llvm::LLVMConstIntGetZExtValue(v) - } -} - -pub fn is_const_integral(v: ValueRef) -> bool { - unsafe { - !llvm::LLVMIsAConstantInt(v).is_null() - } -} - -pub fn is_const_real(v: ValueRef) -> bool { - unsafe { - !llvm::LLVMIsAConstantFP(v).is_null() - } -} - - -#[inline] -fn hi_lo_to_u128(lo: u64, hi: u64) -> u128 { - ((hi as u128) << 64) | (lo as u128) -} - -pub fn const_to_opt_u128(v: ValueRef, sign_ext: bool) -> Option { - unsafe { - if is_const_integral(v) { - let (mut lo, mut hi) = (0u64, 0u64); - let success = llvm::LLVMRustConstInt128Get(v, sign_ext, - &mut hi as *mut u64, &mut lo as *mut u64); - if success { - Some(hi_lo_to_u128(lo, hi)) - } else { - None - } - } else { - None - } - } -} - -pub fn langcall(tcx: TyCtxt, - span: Option, - msg: &str, - li: LangItem) - -> DefId { - match tcx.lang_items().require(li) { - Ok(id) => id, - Err(s) => { - let msg = format!("{} {}", msg, s); - match span { - Some(span) => tcx.sess.span_fatal(span, &msg[..]), - None => tcx.sess.fatal(&msg[..]), - } - } - } -} - -// To avoid UB from LLVM, these two functions mask RHS with an -// appropriate mask unconditionally (i.e. the fallback behavior for -// all shifts). For 32- and 64-bit types, this matches the semantics -// of Java. (See related discussion on #1877 and #10183.) - -pub fn build_unchecked_lshift<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - lhs: ValueRef, - rhs: ValueRef -) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShl, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - bx.shl(lhs, rhs) -} - -pub fn build_unchecked_rshift<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, lhs_t: Ty<'tcx>, lhs: ValueRef, rhs: ValueRef -) -> ValueRef { - let rhs = base::cast_shift_expr_rhs(bx, hir::BinOp_::BiShr, lhs, rhs); - // #1877, #10183: Ensure that input is always valid - let rhs = shift_mask_rhs(bx, rhs); - let is_signed = lhs_t.is_signed(); - if is_signed { - bx.ashr(lhs, rhs) - } else { - bx.lshr(lhs, rhs) - } -} - -fn shift_mask_rhs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, rhs: ValueRef) -> ValueRef { - let rhs_llty = val_ty(rhs); - bx.and(rhs, shift_mask_val(bx, rhs_llty, rhs_llty, false)) -} - -pub fn shift_mask_val<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - llty: Type, - mask_llty: Type, - invert: bool -) -> ValueRef { - let kind = llty.kind(); - match kind { - TypeKind::Integer => { - // i8/u8 can shift by at most 7, i16/u16 by at most 15, etc. - let val = llty.int_width() - 1; - if invert { - C_int(mask_llty, !val as i64) - } else { - C_uint(mask_llty, val) - } - }, - TypeKind::Vector => { - let mask = shift_mask_val(bx, llty.element_type(), mask_llty.element_type(), invert); - bx.vector_splat(mask_llty.vector_length(), mask) - }, - _ => bug!("shift_mask_val: expected Integer or Vector, found {:?}", kind), - } -} - -pub fn ty_fn_sig<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: Ty<'tcx>) - -> ty::PolyFnSig<'tcx> -{ - match ty.sty { - ty::TyFnDef(..) | - // Shims currently have type TyFnPtr. Not sure this should remain. - ty::TyFnPtr(_) => ty.fn_sig(cx.tcx), - ty::TyClosure(def_id, substs) => { - let tcx = cx.tcx; - let sig = substs.closure_sig(def_id, tcx); - - let env_ty = tcx.closure_env_ty(def_id, substs).unwrap(); - sig.map_bound(|sig| tcx.mk_fn_sig( - iter::once(*env_ty.skip_binder()).chain(sig.inputs().iter().cloned()), - sig.output(), - sig.variadic, - sig.unsafety, - sig.abi - )) - } - ty::TyGenerator(def_id, substs, _) => { - let tcx = cx.tcx; - let sig = substs.poly_sig(def_id, cx.tcx); - - let env_region = ty::ReLateBound(ty::DebruijnIndex::new(1), ty::BrEnv); - let env_ty = tcx.mk_mut_ref(tcx.mk_region(env_region), ty); - - sig.map_bound(|sig| { - let state_did = tcx.lang_items().gen_state().unwrap(); - let state_adt_ref = tcx.adt_def(state_did); - let state_substs = tcx.mk_substs([sig.yield_ty.into(), - sig.return_ty.into()].iter()); - let ret_ty = tcx.mk_adt(state_adt_ref, state_substs); - - tcx.mk_fn_sig(iter::once(env_ty), - ret_ty, - false, - hir::Unsafety::Normal, - Abi::Rust - ) - }) - } - _ => bug!("unexpected type {:?} to ty_fn_sig", ty) - } -} - diff --git a/src/librustc_trans/consts.rs b/src/librustc_trans/consts.rs deleted file mode 100644 index 405cb83ad4d..00000000000 --- a/src/librustc_trans/consts.rs +++ /dev/null @@ -1,322 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; -use llvm::{SetUnnamedAddr}; -use llvm::{ValueRef, True}; -use rustc::hir::def_id::DefId; -use rustc::hir::map as hir_map; -use debuginfo; -use base; -use monomorphize::MonoItem; -use common::{CodegenCx, val_ty}; -use declare; -use monomorphize::Instance; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::ty; -use rustc::ty::layout::{Align, LayoutOf}; - -use rustc::hir; - -use std::ffi::{CStr, CString}; -use syntax::ast; -use syntax::attr; - -pub fn ptrcast(val: ValueRef, ty: Type) -> ValueRef { - unsafe { - llvm::LLVMConstPointerCast(val, ty.to_ref()) - } -} - -pub fn bitcast(val: ValueRef, ty: Type) -> ValueRef { - unsafe { - llvm::LLVMConstBitCast(val, ty.to_ref()) - } -} - -fn set_global_alignment(cx: &CodegenCx, - gv: ValueRef, - mut align: Align) { - // The target may require greater alignment for globals than the type does. - // Note: GCC and Clang also allow `__attribute__((aligned))` on variables, - // which can force it to be smaller. Rust doesn't support this yet. - if let Some(min) = cx.sess().target.target.options.min_global_align { - match ty::layout::Align::from_bits(min, min) { - Ok(min) => align = align.max(min), - Err(err) => { - cx.sess().err(&format!("invalid minimum global alignment: {}", err)); - } - } - } - unsafe { - llvm::LLVMSetAlignment(gv, align.abi() as u32); - } -} - -pub fn addr_of_mut(cx: &CodegenCx, - cv: ValueRef, - align: Align, - kind: &str) - -> ValueRef { - unsafe { - let name = cx.generate_local_symbol_name(kind); - let gv = declare::define_global(cx, &name[..], val_ty(cv)).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", name); - }); - llvm::LLVMSetInitializer(gv, cv); - set_global_alignment(cx, gv, align); - llvm::LLVMRustSetLinkage(gv, llvm::Linkage::PrivateLinkage); - SetUnnamedAddr(gv, true); - gv - } -} - -pub fn addr_of(cx: &CodegenCx, - cv: ValueRef, - align: Align, - kind: &str) - -> ValueRef { - if let Some(&gv) = cx.const_globals.borrow().get(&cv) { - unsafe { - // Upgrade the alignment in cases where the same constant is used with different - // alignment requirements - let llalign = align.abi() as u32; - if llalign > llvm::LLVMGetAlignment(gv) { - llvm::LLVMSetAlignment(gv, llalign); - } - } - return gv; - } - let gv = addr_of_mut(cx, cv, align, kind); - unsafe { - llvm::LLVMSetGlobalConstant(gv, True); - } - cx.const_globals.borrow_mut().insert(cv, gv); - gv -} - -pub fn get_static(cx: &CodegenCx, def_id: DefId) -> ValueRef { - let instance = Instance::mono(cx.tcx, def_id); - if let Some(&g) = cx.instances.borrow().get(&instance) { - return g; - } - - let defined_in_current_codegen_unit = cx.codegen_unit - .items() - .contains_key(&MonoItem::Static(def_id)); - assert!(!defined_in_current_codegen_unit, - "consts::get_static() should always hit the cache for \ - statics defined in the same CGU, but did not for `{:?}`", - def_id); - - let ty = instance.ty(cx.tcx); - let sym = cx.tcx.symbol_name(instance).as_str(); - - let g = if let Some(id) = cx.tcx.hir.as_local_node_id(def_id) { - - let llty = cx.layout_of(ty).llvm_type(cx); - let (g, attrs) = match cx.tcx.hir.get(id) { - hir_map::NodeItem(&hir::Item { - ref attrs, span, node: hir::ItemStatic(..), .. - }) => { - if declare::get_declared_value(cx, &sym[..]).is_some() { - span_bug!(span, "trans: Conflicting symbol names for static?"); - } - - let g = declare::define_global(cx, &sym[..], llty).unwrap(); - - if !cx.tcx.is_reachable_non_generic(def_id) { - unsafe { - llvm::LLVMRustSetVisibility(g, llvm::Visibility::Hidden); - } - } - - (g, attrs) - } - - hir_map::NodeForeignItem(&hir::ForeignItem { - ref attrs, span, node: hir::ForeignItemStatic(..), .. - }) => { - let g = if let Some(linkage) = cx.tcx.trans_fn_attrs(def_id).linkage { - // If this is a static with a linkage specified, then we need to handle - // it a little specially. The typesystem prevents things like &T and - // extern "C" fn() from being non-null, so we can't just declare a - // static and call it a day. Some linkages (like weak) will make it such - // that the static actually has a null value. - let llty2 = match ty.sty { - ty::TyRawPtr(ref mt) => cx.layout_of(mt.ty).llvm_type(cx), - _ => { - cx.sess().span_fatal(span, "must have type `*const T` or `*mut T`"); - } - }; - unsafe { - // Declare a symbol `foo` with the desired linkage. - let g1 = declare::declare_global(cx, &sym, llty2); - llvm::LLVMRustSetLinkage(g1, base::linkage_to_llvm(linkage)); - - // Declare an internal global `extern_with_linkage_foo` which - // is initialized with the address of `foo`. If `foo` is - // discarded during linking (for example, if `foo` has weak - // linkage and there are no definitions), then - // `extern_with_linkage_foo` will instead be initialized to - // zero. - let mut real_name = "_rust_extern_with_linkage_".to_string(); - real_name.push_str(&sym); - let g2 = declare::define_global(cx, &real_name, llty).unwrap_or_else(||{ - cx.sess().span_fatal(span, - &format!("symbol `{}` is already defined", &sym)) - }); - llvm::LLVMRustSetLinkage(g2, llvm::Linkage::InternalLinkage); - llvm::LLVMSetInitializer(g2, g1); - g2 - } - } else { - // Generate an external declaration. - declare::declare_global(cx, &sym, llty) - }; - - (g, attrs) - } - - item => bug!("get_static: expected static, found {:?}", item) - }; - - for attr in attrs { - if attr.check_name("thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - } - - g - } else { - // FIXME(nagisa): perhaps the map of externs could be offloaded to llvm somehow? - // FIXME(nagisa): investigate whether it can be changed into define_global - let g = declare::declare_global(cx, &sym, cx.layout_of(ty).llvm_type(cx)); - // Thread-local statics in some other crate need to *always* be linked - // against in a thread-local fashion, so we need to be sure to apply the - // thread-local attribute locally if it was present remotely. If we - // don't do this then linker errors can be generated where the linker - // complains that one object files has a thread local version of the - // symbol and another one doesn't. - for attr in cx.tcx.get_attrs(def_id).iter() { - if attr.check_name("thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - } - if cx.use_dll_storage_attrs && !cx.tcx.is_foreign_item(def_id) { - // This item is external but not foreign, i.e. it originates from an external Rust - // crate. Since we don't know whether this crate will be linked dynamically or - // statically in the final application, we always mark such symbols as 'dllimport'. - // If final linkage happens to be static, we rely on compiler-emitted __imp_ stubs to - // make things work. - // - // However, in some scenarios we defer emission of statics to downstream - // crates, so there are cases where a static with an upstream DefId - // is actually present in the current crate. We can find out via the - // is_translated_item query. - if !cx.tcx.is_translated_item(def_id) { - unsafe { - llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); - } - } - } - g - }; - - if cx.use_dll_storage_attrs && cx.tcx.is_dllimport_foreign_item(def_id) { - // For foreign (native) libs we know the exact storage type to use. - unsafe { - llvm::LLVMSetDLLStorageClass(g, llvm::DLLStorageClass::DllImport); - } - } - - cx.instances.borrow_mut().insert(instance, g); - cx.statics.borrow_mut().insert(g, def_id); - g -} - -pub fn trans_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - is_mutable: bool, - attrs: &[ast::Attribute]) { - unsafe { - let g = get_static(cx, def_id); - - let v = match ::mir::trans_static_initializer(cx, def_id) { - Ok(v) => v, - // Error has already been reported - Err(_) => return, - }; - - // boolean SSA values are i1, but they have to be stored in i8 slots, - // otherwise some LLVM optimization passes don't work as expected - let mut val_llty = val_ty(v); - let v = if val_llty == Type::i1(cx) { - val_llty = Type::i8(cx); - llvm::LLVMConstZExt(v, val_llty.to_ref()) - } else { - v - }; - - let instance = Instance::mono(cx.tcx, def_id); - let ty = instance.ty(cx.tcx); - let llty = cx.layout_of(ty).llvm_type(cx); - let g = if val_llty == llty { - g - } else { - // If we created the global with the wrong type, - // correct the type. - let empty_string = CString::new("").unwrap(); - let name_str_ref = CStr::from_ptr(llvm::LLVMGetValueName(g)); - let name_string = CString::new(name_str_ref.to_bytes()).unwrap(); - llvm::LLVMSetValueName(g, empty_string.as_ptr()); - - let linkage = llvm::LLVMRustGetLinkage(g); - let visibility = llvm::LLVMRustGetVisibility(g); - - let new_g = llvm::LLVMRustGetOrInsertGlobal( - cx.llmod, name_string.as_ptr(), val_llty.to_ref()); - - llvm::LLVMRustSetLinkage(new_g, linkage); - llvm::LLVMRustSetVisibility(new_g, visibility); - - // To avoid breaking any invariants, we leave around the old - // global for the moment; we'll replace all references to it - // with the new global later. (See base::trans_crate.) - cx.statics_to_rauw.borrow_mut().push((g, new_g)); - new_g - }; - set_global_alignment(cx, g, cx.align_of(ty)); - llvm::LLVMSetInitializer(g, v); - - // As an optimization, all shared statics which do not have interior - // mutability are placed into read-only memory. - if !is_mutable { - if cx.type_is_freeze(ty) { - llvm::LLVMSetGlobalConstant(g, llvm::True); - } - } - - debuginfo::create_global_var_metadata(cx, def_id, g); - - if attr::contains_name(attrs, "thread_local") { - llvm::set_thread_local_mode(g, cx.tls_model); - } - - base::set_link_section(cx, g, attrs); - - if attr::contains_name(attrs, "used") { - // This static will be stored in the llvm.used variable which is an array of i8* - let cast = llvm::LLVMConstPointerCast(g, Type::i8p(cx).to_ref()); - cx.used_statics.borrow_mut().push(cast); - } - } -} diff --git a/src/librustc_trans/context.rs b/src/librustc_trans/context.rs deleted file mode 100644 index 90b2fb4b59a..00000000000 --- a/src/librustc_trans/context.rs +++ /dev/null @@ -1,666 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use common; -use llvm; -use llvm::{ContextRef, ModuleRef, ValueRef}; -use rustc::dep_graph::DepGraphSafe; -use rustc::hir; -use rustc::hir::def_id::DefId; -use debuginfo; -use callee; -use base; -use declare; -use monomorphize::Instance; - -use monomorphize::partitioning::CodegenUnit; -use type_::Type; -use type_of::PointeeInfo; - -use rustc_data_structures::base_n; -use rustc::mir::mono::Stats; -use rustc::session::config::{self, NoDebugInfo}; -use rustc::session::Session; -use rustc::ty::layout::{LayoutError, LayoutOf, Size, TyLayout}; -use rustc::ty::{self, Ty, TyCtxt}; -use rustc::util::nodemap::FxHashMap; -use rustc_target::spec::{HasTargetSpec, Target}; - -use std::ffi::{CStr, CString}; -use std::cell::{Cell, RefCell}; -use std::ptr; -use std::iter; -use std::str; -use std::sync::Arc; -use syntax::symbol::LocalInternedString; -use abi::Abi; - -/// There is one `CodegenCx` per compilation unit. Each one has its own LLVM -/// `ContextRef` so that several compilation units may be optimized in parallel. -/// All other LLVM data structures in the `CodegenCx` are tied to that `ContextRef`. -pub struct CodegenCx<'a, 'tcx: 'a> { - pub tcx: TyCtxt<'a, 'tcx, 'tcx>, - pub check_overflow: bool, - pub use_dll_storage_attrs: bool, - pub tls_model: llvm::ThreadLocalMode, - - pub llmod: ModuleRef, - pub llcx: ContextRef, - pub stats: RefCell, - pub codegen_unit: Arc>, - - /// Cache instances of monomorphic and polymorphic items - pub instances: RefCell, ValueRef>>, - /// Cache generated vtables - pub vtables: RefCell, - Option>), ValueRef>>, - /// Cache of constant strings, - pub const_cstr_cache: RefCell>, - - /// Reverse-direction for const ptrs cast from globals. - /// Key is a ValueRef holding a *T, - /// Val is a ValueRef holding a *[T]. - /// - /// Needed because LLVM loses pointer->pointee association - /// when we ptrcast, and we have to ptrcast during translation - /// of a [T] const because we form a slice, a (*T,usize) pair, not - /// a pointer to an LLVM array type. Similar for trait objects. - pub const_unsized: RefCell>, - - /// Cache of emitted const globals (value -> global) - pub const_globals: RefCell>, - - /// Mapping from static definitions to their DefId's. - pub statics: RefCell>, - - /// List of globals for static variables which need to be passed to the - /// LLVM function ReplaceAllUsesWith (RAUW) when translation is complete. - /// (We have to make sure we don't invalidate any ValueRefs referring - /// to constants.) - pub statics_to_rauw: RefCell>, - - /// Statics that will be placed in the llvm.used variable - /// See http://llvm.org/docs/LangRef.html#the-llvm-used-global-variable for details - pub used_statics: RefCell>, - - pub lltypes: RefCell, Option), Type>>, - pub scalar_lltypes: RefCell, Type>>, - pub pointee_infos: RefCell, Size), Option>>, - pub isize_ty: Type, - - pub dbg_cx: Option>, - - eh_personality: Cell>, - eh_unwind_resume: Cell>, - pub rust_try_fn: Cell>, - - intrinsics: RefCell>, - - /// A counter that is used for generating local symbol names - local_gen_sym_counter: Cell, -} - -impl<'a, 'tcx> DepGraphSafe for CodegenCx<'a, 'tcx> { -} - -pub fn get_reloc_model(sess: &Session) -> llvm::RelocMode { - let reloc_model_arg = match sess.opts.cg.relocation_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.relocation_model[..], - }; - - match ::back::write::RELOC_MODEL_ARGS.iter().find( - |&&arg| arg.0 == reloc_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid relocation mode", - reloc_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } -} - -fn get_tls_model(sess: &Session) -> llvm::ThreadLocalMode { - let tls_model_arg = match sess.opts.debugging_opts.tls_model { - Some(ref s) => &s[..], - None => &sess.target.target.options.tls_model[..], - }; - - match ::back::write::TLS_MODEL_ARGS.iter().find( - |&&arg| arg.0 == tls_model_arg) { - Some(x) => x.1, - _ => { - sess.err(&format!("{:?} is not a valid TLS model", - tls_model_arg)); - sess.abort_if_errors(); - bug!(); - } - } -} - -fn is_any_library(sess: &Session) -> bool { - sess.crate_types.borrow().iter().any(|ty| { - *ty != config::CrateTypeExecutable - }) -} - -pub fn is_pie_binary(sess: &Session) -> bool { - !is_any_library(sess) && get_reloc_model(sess) == llvm::RelocMode::PIC -} - -pub unsafe fn create_context_and_module(sess: &Session, mod_name: &str) -> (ContextRef, ModuleRef) { - let llcx = llvm::LLVMRustContextCreate(sess.fewer_names()); - let mod_name = CString::new(mod_name).unwrap(); - let llmod = llvm::LLVMModuleCreateWithNameInContext(mod_name.as_ptr(), llcx); - - // Ensure the data-layout values hardcoded remain the defaults. - if sess.target.target.options.is_builtin { - let tm = ::back::write::create_target_machine(sess, false); - llvm::LLVMRustSetDataLayoutFromTargetMachine(llmod, tm); - llvm::LLVMRustDisposeTargetMachine(tm); - - let data_layout = llvm::LLVMGetDataLayout(llmod); - let data_layout = str::from_utf8(CStr::from_ptr(data_layout).to_bytes()) - .ok().expect("got a non-UTF8 data-layout from LLVM"); - - // Unfortunately LLVM target specs change over time, and right now we - // don't have proper support to work with any more than one - // `data_layout` than the one that is in the rust-lang/rust repo. If - // this compiler is configured against a custom LLVM, we may have a - // differing data layout, even though we should update our own to use - // that one. - // - // As an interim hack, if CFG_LLVM_ROOT is not an empty string then we - // disable this check entirely as we may be configured with something - // that has a different target layout. - // - // Unsure if this will actually cause breakage when rustc is configured - // as such. - // - // FIXME(#34960) - let cfg_llvm_root = option_env!("CFG_LLVM_ROOT").unwrap_or(""); - let custom_llvm_used = cfg_llvm_root.trim() != ""; - - if !custom_llvm_used && sess.target.target.data_layout != data_layout { - bug!("data-layout for builtin `{}` target, `{}`, \ - differs from LLVM default, `{}`", - sess.target.target.llvm_target, - sess.target.target.data_layout, - data_layout); - } - } - - let data_layout = CString::new(&sess.target.target.data_layout[..]).unwrap(); - llvm::LLVMSetDataLayout(llmod, data_layout.as_ptr()); - - let llvm_target = sess.target.target.llvm_target.as_bytes(); - let llvm_target = CString::new(llvm_target).unwrap(); - llvm::LLVMRustSetNormalizedTarget(llmod, llvm_target.as_ptr()); - - if is_pie_binary(sess) { - llvm::LLVMRustSetModulePIELevel(llmod); - } - - (llcx, llmod) -} - -impl<'a, 'tcx> CodegenCx<'a, 'tcx> { - pub fn new(tcx: TyCtxt<'a, 'tcx, 'tcx>, - codegen_unit: Arc>, - llmod_id: &str) - -> CodegenCx<'a, 'tcx> { - // An interesting part of Windows which MSVC forces our hand on (and - // apparently MinGW didn't) is the usage of `dllimport` and `dllexport` - // attributes in LLVM IR as well as native dependencies (in C these - // correspond to `__declspec(dllimport)`). - // - // Whenever a dynamic library is built by MSVC it must have its public - // interface specified by functions tagged with `dllexport` or otherwise - // they're not available to be linked against. This poses a few problems - // for the compiler, some of which are somewhat fundamental, but we use - // the `use_dll_storage_attrs` variable below to attach the `dllexport` - // attribute to all LLVM functions that are exported e.g. they're - // already tagged with external linkage). This is suboptimal for a few - // reasons: - // - // * If an object file will never be included in a dynamic library, - // there's no need to attach the dllexport attribute. Most object - // files in Rust are not destined to become part of a dll as binaries - // are statically linked by default. - // * If the compiler is emitting both an rlib and a dylib, the same - // source object file is currently used but with MSVC this may be less - // feasible. The compiler may be able to get around this, but it may - // involve some invasive changes to deal with this. - // - // The flipside of this situation is that whenever you link to a dll and - // you import a function from it, the import should be tagged with - // `dllimport`. At this time, however, the compiler does not emit - // `dllimport` for any declarations other than constants (where it is - // required), which is again suboptimal for even more reasons! - // - // * Calling a function imported from another dll without using - // `dllimport` causes the linker/compiler to have extra overhead (one - // `jmp` instruction on x86) when calling the function. - // * The same object file may be used in different circumstances, so a - // function may be imported from a dll if the object is linked into a - // dll, but it may be just linked against if linked into an rlib. - // * The compiler has no knowledge about whether native functions should - // be tagged dllimport or not. - // - // For now the compiler takes the perf hit (I do not have any numbers to - // this effect) by marking very little as `dllimport` and praying the - // linker will take care of everything. Fixing this problem will likely - // require adding a few attributes to Rust itself (feature gated at the - // start) and then strongly recommending static linkage on MSVC! - let use_dll_storage_attrs = tcx.sess.target.target.options.is_like_msvc; - - let check_overflow = tcx.sess.overflow_checks(); - - let tls_model = get_tls_model(&tcx.sess); - - unsafe { - let (llcx, llmod) = create_context_and_module(&tcx.sess, - &llmod_id[..]); - - let dbg_cx = if tcx.sess.opts.debuginfo != NoDebugInfo { - let dctx = debuginfo::CrateDebugContext::new(llmod); - debuginfo::metadata::compile_unit_metadata(tcx, - &codegen_unit.name().as_str(), - &dctx); - Some(dctx) - } else { - None - }; - - let mut cx = CodegenCx { - tcx, - check_overflow, - use_dll_storage_attrs, - tls_model, - llmod, - llcx, - stats: RefCell::new(Stats::default()), - codegen_unit, - instances: RefCell::new(FxHashMap()), - vtables: RefCell::new(FxHashMap()), - const_cstr_cache: RefCell::new(FxHashMap()), - const_unsized: RefCell::new(FxHashMap()), - const_globals: RefCell::new(FxHashMap()), - statics: RefCell::new(FxHashMap()), - statics_to_rauw: RefCell::new(Vec::new()), - used_statics: RefCell::new(Vec::new()), - lltypes: RefCell::new(FxHashMap()), - scalar_lltypes: RefCell::new(FxHashMap()), - pointee_infos: RefCell::new(FxHashMap()), - isize_ty: Type::from_ref(ptr::null_mut()), - dbg_cx, - eh_personality: Cell::new(None), - eh_unwind_resume: Cell::new(None), - rust_try_fn: Cell::new(None), - intrinsics: RefCell::new(FxHashMap()), - local_gen_sym_counter: Cell::new(0), - }; - cx.isize_ty = Type::isize(&cx); - cx - } - } - - pub fn into_stats(self) -> Stats { - self.stats.into_inner() - } -} - -impl<'b, 'tcx> CodegenCx<'b, 'tcx> { - pub fn sess<'a>(&'a self) -> &'a Session { - &self.tcx.sess - } - - pub fn get_intrinsic(&self, key: &str) -> ValueRef { - if let Some(v) = self.intrinsics.borrow().get(key).cloned() { - return v; - } - match declare_intrinsic(self, key) { - Some(v) => return v, - None => bug!("unknown intrinsic '{}'", key) - } - } - - /// Generate a new symbol name with the given prefix. This symbol name must - /// only be used for definitions with `internal` or `private` linkage. - pub fn generate_local_symbol_name(&self, prefix: &str) -> String { - let idx = self.local_gen_sym_counter.get(); - self.local_gen_sym_counter.set(idx + 1); - // Include a '.' character, so there can be no accidental conflicts with - // user defined names - let mut name = String::with_capacity(prefix.len() + 6); - name.push_str(prefix); - name.push_str("."); - base_n::push_str(idx as u128, base_n::ALPHANUMERIC_ONLY, &mut name); - name - } - - pub fn eh_personality(&self) -> ValueRef { - // The exception handling personality function. - // - // If our compilation unit has the `eh_personality` lang item somewhere - // within it, then we just need to translate that. Otherwise, we're - // building an rlib which will depend on some upstream implementation of - // this function, so we just codegen a generic reference to it. We don't - // specify any of the types for the function, we just make it a symbol - // that LLVM can later use. - // - // Note that MSVC is a little special here in that we don't use the - // `eh_personality` lang item at all. Currently LLVM has support for - // both Dwarf and SEH unwind mechanisms for MSVC targets and uses the - // *name of the personality function* to decide what kind of unwind side - // tables/landing pads to emit. It looks like Dwarf is used by default, - // injecting a dependency on the `_Unwind_Resume` symbol for resuming - // an "exception", but for MSVC we want to force SEH. This means that we - // can't actually have the personality function be our standard - // `rust_eh_personality` function, but rather we wired it up to the - // CRT's custom personality function, which forces LLVM to consider - // landing pads as "landing pads for SEH". - if let Some(llpersonality) = self.eh_personality.get() { - return llpersonality - } - let tcx = self.tcx; - let llfn = match tcx.lang_items().eh_personality() { - Some(def_id) if !base::wants_msvc_seh(self.sess()) => { - callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])) - } - _ => { - let name = if base::wants_msvc_seh(self.sess()) { - "__CxxFrameHandler3" - } else { - "rust_eh_personality" - }; - let fty = Type::variadic_func(&[], &Type::i32(self)); - declare::declare_cfn(self, name, fty) - } - }; - self.eh_personality.set(Some(llfn)); - llfn - } - - // Returns a ValueRef of the "eh_unwind_resume" lang item if one is defined, - // otherwise declares it as an external function. - pub fn eh_unwind_resume(&self) -> ValueRef { - use attributes; - let unwresume = &self.eh_unwind_resume; - if let Some(llfn) = unwresume.get() { - return llfn; - } - - let tcx = self.tcx; - assert!(self.sess().target.target.options.custom_unwind_resume); - if let Some(def_id) = tcx.lang_items().eh_unwind_resume() { - let llfn = callee::resolve_and_get_fn(self, def_id, tcx.intern_substs(&[])); - unwresume.set(Some(llfn)); - return llfn; - } - - let ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::once(tcx.mk_mut_ptr(tcx.types.u8)), - tcx.types.never, - false, - hir::Unsafety::Unsafe, - Abi::C - ))); - - let llfn = declare::declare_fn(self, "rust_eh_unwind_resume", ty); - attributes::unwind(llfn, true); - unwresume.set(Some(llfn)); - llfn - } - - pub fn type_needs_drop(&self, ty: Ty<'tcx>) -> bool { - common::type_needs_drop(self.tcx, ty) - } - - pub fn type_is_sized(&self, ty: Ty<'tcx>) -> bool { - common::type_is_sized(self.tcx, ty) - } - - pub fn type_is_freeze(&self, ty: Ty<'tcx>) -> bool { - common::type_is_freeze(self.tcx, ty) - } - - pub fn type_has_metadata(&self, ty: Ty<'tcx>) -> bool { - use syntax_pos::DUMMY_SP; - if ty.is_sized(self.tcx.at(DUMMY_SP), ty::ParamEnv::reveal_all()) { - return false; - } - - let tail = self.tcx.struct_tail(ty); - match tail.sty { - ty::TyForeign(..) => false, - ty::TyStr | ty::TySlice(..) | ty::TyDynamic(..) => true, - _ => bug!("unexpected unsized tail: {:?}", tail.sty), - } - } -} - -impl<'a, 'tcx> ty::layout::HasDataLayout for &'a CodegenCx<'a, 'tcx> { - fn data_layout(&self) -> &ty::layout::TargetDataLayout { - &self.tcx.data_layout - } -} - -impl<'a, 'tcx> HasTargetSpec for &'a CodegenCx<'a, 'tcx> { - fn target_spec(&self) -> &Target { - &self.tcx.sess.target.target - } -} - -impl<'a, 'tcx> ty::layout::HasTyCtxt<'tcx> for &'a CodegenCx<'a, 'tcx> { - fn tcx<'b>(&'b self) -> TyCtxt<'b, 'tcx, 'tcx> { - self.tcx - } -} - -impl<'a, 'tcx> LayoutOf for &'a CodegenCx<'a, 'tcx> { - type Ty = Ty<'tcx>; - type TyLayout = TyLayout<'tcx>; - - fn layout_of(self, ty: Ty<'tcx>) -> Self::TyLayout { - self.tcx.layout_of(ty::ParamEnv::reveal_all().and(ty)) - .unwrap_or_else(|e| match e { - LayoutError::SizeOverflow(_) => self.sess().fatal(&e.to_string()), - _ => bug!("failed to get layout for `{}`: {}", ty, e) - }) - } -} - -/// Declare any llvm intrinsics that you might need -fn declare_intrinsic(cx: &CodegenCx, key: &str) -> Option { - macro_rules! ifn { - ($name:expr, fn() -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::func(&[], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn(...) -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::variadic_func(&[], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - ($name:expr, fn($($arg:expr),*) -> $ret:expr) => ( - if key == $name { - let f = declare::declare_cfn(cx, $name, Type::func(&[$($arg),*], &$ret)); - llvm::SetUnnamedAddr(f, false); - cx.intrinsics.borrow_mut().insert($name, f.clone()); - return Some(f); - } - ); - } - macro_rules! mk_struct { - ($($field_ty:expr),*) => (Type::struct_(cx, &[$($field_ty),*], false)) - } - - let i8p = Type::i8p(cx); - let void = Type::void(cx); - let i1 = Type::i1(cx); - let t_i8 = Type::i8(cx); - let t_i16 = Type::i16(cx); - let t_i32 = Type::i32(cx); - let t_i64 = Type::i64(cx); - let t_i128 = Type::i128(cx); - let t_f32 = Type::f32(cx); - let t_f64 = Type::f64(cx); - - ifn!("llvm.memcpy.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memcpy.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i16", fn(i8p, i8p, t_i16, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i32", fn(i8p, i8p, t_i32, t_i32, i1) -> void); - ifn!("llvm.memmove.p0i8.p0i8.i64", fn(i8p, i8p, t_i64, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i16", fn(i8p, t_i8, t_i16, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i32", fn(i8p, t_i8, t_i32, t_i32, i1) -> void); - ifn!("llvm.memset.p0i8.i64", fn(i8p, t_i8, t_i64, t_i32, i1) -> void); - - ifn!("llvm.trap", fn() -> void); - ifn!("llvm.debugtrap", fn() -> void); - ifn!("llvm.frameaddress", fn(t_i32) -> i8p); - - ifn!("llvm.powi.f32", fn(t_f32, t_i32) -> t_f32); - ifn!("llvm.powi.f64", fn(t_f64, t_i32) -> t_f64); - ifn!("llvm.pow.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.pow.f64", fn(t_f64, t_f64) -> t_f64); - - ifn!("llvm.sqrt.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sqrt.f64", fn(t_f64) -> t_f64); - ifn!("llvm.sin.f32", fn(t_f32) -> t_f32); - ifn!("llvm.sin.f64", fn(t_f64) -> t_f64); - ifn!("llvm.cos.f32", fn(t_f32) -> t_f32); - ifn!("llvm.cos.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp.f64", fn(t_f64) -> t_f64); - ifn!("llvm.exp2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.exp2.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log10.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log10.f64", fn(t_f64) -> t_f64); - ifn!("llvm.log2.f32", fn(t_f32) -> t_f32); - ifn!("llvm.log2.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.fma.f32", fn(t_f32, t_f32, t_f32) -> t_f32); - ifn!("llvm.fma.f64", fn(t_f64, t_f64, t_f64) -> t_f64); - - ifn!("llvm.fabs.f32", fn(t_f32) -> t_f32); - ifn!("llvm.fabs.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.floor.f32", fn(t_f32) -> t_f32); - ifn!("llvm.floor.f64", fn(t_f64) -> t_f64); - ifn!("llvm.ceil.f32", fn(t_f32) -> t_f32); - ifn!("llvm.ceil.f64", fn(t_f64) -> t_f64); - ifn!("llvm.trunc.f32", fn(t_f32) -> t_f32); - ifn!("llvm.trunc.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.copysign.f32", fn(t_f32, t_f32) -> t_f32); - ifn!("llvm.copysign.f64", fn(t_f64, t_f64) -> t_f64); - ifn!("llvm.round.f32", fn(t_f32) -> t_f32); - ifn!("llvm.round.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.rint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.rint.f64", fn(t_f64) -> t_f64); - ifn!("llvm.nearbyint.f32", fn(t_f32) -> t_f32); - ifn!("llvm.nearbyint.f64", fn(t_f64) -> t_f64); - - ifn!("llvm.ctpop.i8", fn(t_i8) -> t_i8); - ifn!("llvm.ctpop.i16", fn(t_i16) -> t_i16); - ifn!("llvm.ctpop.i32", fn(t_i32) -> t_i32); - ifn!("llvm.ctpop.i64", fn(t_i64) -> t_i64); - ifn!("llvm.ctpop.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.ctlz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.ctlz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.ctlz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.ctlz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.ctlz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.cttz.i8", fn(t_i8 , i1) -> t_i8); - ifn!("llvm.cttz.i16", fn(t_i16, i1) -> t_i16); - ifn!("llvm.cttz.i32", fn(t_i32, i1) -> t_i32); - ifn!("llvm.cttz.i64", fn(t_i64, i1) -> t_i64); - ifn!("llvm.cttz.i128", fn(t_i128, i1) -> t_i128); - - ifn!("llvm.bswap.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bswap.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bswap.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bswap.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.bitreverse.i8", fn(t_i8) -> t_i8); - ifn!("llvm.bitreverse.i16", fn(t_i16) -> t_i16); - ifn!("llvm.bitreverse.i32", fn(t_i32) -> t_i32); - ifn!("llvm.bitreverse.i64", fn(t_i64) -> t_i64); - ifn!("llvm.bitreverse.i128", fn(t_i128) -> t_i128); - - ifn!("llvm.sadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.sadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.sadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.sadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.sadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.uadd.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.uadd.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.uadd.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.uadd.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.uadd.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.ssub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.ssub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.ssub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.ssub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.ssub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.usub.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.usub.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.usub.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.usub.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.usub.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.smul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.smul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.smul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.smul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.smul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.umul.with.overflow.i8", fn(t_i8, t_i8) -> mk_struct!{t_i8, i1}); - ifn!("llvm.umul.with.overflow.i16", fn(t_i16, t_i16) -> mk_struct!{t_i16, i1}); - ifn!("llvm.umul.with.overflow.i32", fn(t_i32, t_i32) -> mk_struct!{t_i32, i1}); - ifn!("llvm.umul.with.overflow.i64", fn(t_i64, t_i64) -> mk_struct!{t_i64, i1}); - ifn!("llvm.umul.with.overflow.i128", fn(t_i128, t_i128) -> mk_struct!{t_i128, i1}); - - ifn!("llvm.lifetime.start", fn(t_i64,i8p) -> void); - ifn!("llvm.lifetime.end", fn(t_i64, i8p) -> void); - - ifn!("llvm.expect.i1", fn(i1, i1) -> i1); - ifn!("llvm.eh.typeid.for", fn(i8p) -> t_i32); - ifn!("llvm.localescape", fn(...) -> void); - ifn!("llvm.localrecover", fn(i8p, i8p, t_i32) -> i8p); - ifn!("llvm.x86.seh.recoverfp", fn(i8p, i8p) -> i8p); - - ifn!("llvm.assume", fn(i1) -> void); - ifn!("llvm.prefetch", fn(i8p, t_i32, t_i32, t_i32) -> void); - - if cx.sess().opts.debuginfo != NoDebugInfo { - ifn!("llvm.dbg.declare", fn(Type::metadata(cx), Type::metadata(cx)) -> void); - ifn!("llvm.dbg.value", fn(Type::metadata(cx), t_i64, Type::metadata(cx)) -> void); - } - return None; -} diff --git a/src/librustc_trans/debuginfo/create_scope_map.rs b/src/librustc_trans/debuginfo/create_scope_map.rs deleted file mode 100644 index bddb3d90940..00000000000 --- a/src/librustc_trans/debuginfo/create_scope_map.rs +++ /dev/null @@ -1,136 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use super::{FunctionDebugContext, FunctionDebugContextData}; -use super::metadata::file_metadata; -use super::utils::{DIB, span_start}; - -use llvm; -use llvm::debuginfo::DIScope; -use common::CodegenCx; -use rustc::mir::{Mir, VisibilityScope}; - -use libc::c_uint; -use std::ptr; - -use syntax_pos::Pos; - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; - -use syntax_pos::BytePos; - -#[derive(Clone, Copy, Debug)] -pub struct MirDebugScope { - pub scope_metadata: DIScope, - // Start and end offsets of the file to which this DIScope belongs. - // These are used to quickly determine whether some span refers to the same file. - pub file_start_pos: BytePos, - pub file_end_pos: BytePos, -} - -impl MirDebugScope { - pub fn is_valid(&self) -> bool { - !self.scope_metadata.is_null() - } -} - -/// Produce DIScope DIEs for each MIR Scope which has variables defined in it. -/// If debuginfo is disabled, the returned vector is empty. -pub fn create_mir_scopes(cx: &CodegenCx, mir: &Mir, debug_context: &FunctionDebugContext) - -> IndexVec { - let null_scope = MirDebugScope { - scope_metadata: ptr::null_mut(), - file_start_pos: BytePos(0), - file_end_pos: BytePos(0) - }; - let mut scopes = IndexVec::from_elem(null_scope, &mir.visibility_scopes); - - let debug_context = match *debug_context { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return scopes; - } - }; - - // Find all the scopes with variables defined in them. - let mut has_variables = BitVector::new(mir.visibility_scopes.len()); - for var in mir.vars_iter() { - let decl = &mir.local_decls[var]; - has_variables.insert(decl.source_info.scope.index()); - } - - // Instantiate all scopes. - for idx in 0..mir.visibility_scopes.len() { - let scope = VisibilityScope::new(idx); - make_mir_scope(cx, &mir, &has_variables, debug_context, scope, &mut scopes); - } - - scopes -} - -fn make_mir_scope(cx: &CodegenCx, - mir: &Mir, - has_variables: &BitVector, - debug_context: &FunctionDebugContextData, - scope: VisibilityScope, - scopes: &mut IndexVec) { - if scopes[scope].is_valid() { - return; - } - - let scope_data = &mir.visibility_scopes[scope]; - let parent_scope = if let Some(parent) = scope_data.parent_scope { - make_mir_scope(cx, mir, has_variables, debug_context, parent, scopes); - scopes[parent] - } else { - // The root is the function itself. - let loc = span_start(cx, mir.span); - scopes[scope] = MirDebugScope { - scope_metadata: debug_context.fn_metadata, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_pos, - }; - return; - }; - - if !has_variables.contains(scope.index()) { - // Do not create a DIScope if there are no variables - // defined in this MIR Scope, to avoid debuginfo bloat. - - // However, we don't skip creating a nested scope if - // our parent is the root, because we might want to - // put arguments in the root and not have shadowing. - if parent_scope.scope_metadata != debug_context.fn_metadata { - scopes[scope] = parent_scope; - return; - } - } - - let loc = span_start(cx, scope_data.span); - let file_metadata = file_metadata(cx, - &loc.file.name, - debug_context.defining_crate); - - let scope_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlock( - DIB(cx), - parent_scope.scope_metadata, - file_metadata, - loc.line as c_uint, - loc.col.to_usize() as c_uint) - }; - scopes[scope] = MirDebugScope { - scope_metadata, - file_start_pos: loc.file.start_pos, - file_end_pos: loc.file.end_pos, - }; -} diff --git a/src/librustc_trans/debuginfo/doc.rs b/src/librustc_trans/debuginfo/doc.rs deleted file mode 100644 index cbecc0eb7d1..00000000000 --- a/src/librustc_trans/debuginfo/doc.rs +++ /dev/null @@ -1,189 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Debug Info Module -//! -//! This module serves the purpose of generating debug symbols. We use LLVM's -//! [source level debugging](http://!llvm.org/docs/SourceLevelDebugging.html) -//! features for generating the debug information. The general principle is -//! this: -//! -//! Given the right metadata in the LLVM IR, the LLVM code generator is able to -//! create DWARF debug symbols for the given code. The -//! [metadata](http://!llvm.org/docs/LangRef.html#metadata-type) is structured -//! much like DWARF *debugging information entries* (DIE), representing type -//! information such as datatype layout, function signatures, block layout, -//! variable location and scope information, etc. It is the purpose of this -//! module to generate correct metadata and insert it into the LLVM IR. -//! -//! As the exact format of metadata trees may change between different LLVM -//! versions, we now use LLVM -//! [DIBuilder](http://!llvm.org/docs/doxygen/html/classllvm_1_1DIBuilder.html) -//! to create metadata where possible. This will hopefully ease the adaption of -//! this module to future LLVM versions. -//! -//! The public API of the module is a set of functions that will insert the -//! correct metadata into the LLVM IR when called with the right parameters. -//! The module is thus driven from an outside client with functions like -//! `debuginfo::create_local_var_metadata(bx: block, local: &ast::local)`. -//! -//! Internally the module will try to reuse already created metadata by -//! utilizing a cache. The way to get a shared metadata node when needed is -//! thus to just call the corresponding function in this module: -//! -//! let file_metadata = file_metadata(crate_context, path); -//! -//! The function will take care of probing the cache for an existing node for -//! that exact file path. -//! -//! All private state used by the module is stored within either the -//! CrateDebugContext struct (owned by the CodegenCx) or the -//! FunctionDebugContext (owned by the FunctionCx). -//! -//! This file consists of three conceptual sections: -//! 1. The public interface of the module -//! 2. Module-internal metadata creation functions -//! 3. Minor utility functions -//! -//! -//! ## Recursive Types -//! -//! Some kinds of types, such as structs and enums can be recursive. That means -//! that the type definition of some type X refers to some other type which in -//! turn (transitively) refers to X. This introduces cycles into the type -//! referral graph. A naive algorithm doing an on-demand, depth-first traversal -//! of this graph when describing types, can get trapped in an endless loop -//! when it reaches such a cycle. -//! -//! For example, the following simple type for a singly-linked list... -//! -//! ``` -//! struct List { -//! value: i32, -//! tail: Option>, -//! } -//! ``` -//! -//! will generate the following callstack with a naive DFS algorithm: -//! -//! ``` -//! describe(t = List) -//! describe(t = i32) -//! describe(t = Option>) -//! describe(t = Box) -//! describe(t = List) // at the beginning again... -//! ... -//! ``` -//! -//! To break cycles like these, we use "forward declarations". That is, when -//! the algorithm encounters a possibly recursive type (any struct or enum), it -//! immediately creates a type description node and inserts it into the cache -//! *before* describing the members of the type. This type description is just -//! a stub (as type members are not described and added to it yet) but it -//! allows the algorithm to already refer to the type. After the stub is -//! inserted into the cache, the algorithm continues as before. If it now -//! encounters a recursive reference, it will hit the cache and does not try to -//! describe the type anew. -//! -//! This behavior is encapsulated in the 'RecursiveTypeDescription' enum, -//! which represents a kind of continuation, storing all state needed to -//! continue traversal at the type members after the type has been registered -//! with the cache. (This implementation approach might be a tad over- -//! engineered and may change in the future) -//! -//! -//! ## Source Locations and Line Information -//! -//! In addition to data type descriptions the debugging information must also -//! allow to map machine code locations back to source code locations in order -//! to be useful. This functionality is also handled in this module. The -//! following functions allow to control source mappings: -//! -//! + set_source_location() -//! + clear_source_location() -//! + start_emitting_source_locations() -//! -//! `set_source_location()` allows to set the current source location. All IR -//! instructions created after a call to this function will be linked to the -//! given source location, until another location is specified with -//! `set_source_location()` or the source location is cleared with -//! `clear_source_location()`. In the later case, subsequent IR instruction -//! will not be linked to any source location. As you can see, this is a -//! stateful API (mimicking the one in LLVM), so be careful with source -//! locations set by previous calls. It's probably best to not rely on any -//! specific state being present at a given point in code. -//! -//! One topic that deserves some extra attention is *function prologues*. At -//! the beginning of a function's machine code there are typically a few -//! instructions for loading argument values into allocas and checking if -//! there's enough stack space for the function to execute. This *prologue* is -//! not visible in the source code and LLVM puts a special PROLOGUE END marker -//! into the line table at the first non-prologue instruction of the function. -//! In order to find out where the prologue ends, LLVM looks for the first -//! instruction in the function body that is linked to a source location. So, -//! when generating prologue instructions we have to make sure that we don't -//! emit source location information until the 'real' function body begins. For -//! this reason, source location emission is disabled by default for any new -//! function being translated and is only activated after a call to the third -//! function from the list above, `start_emitting_source_locations()`. This -//! function should be called right before regularly starting to translate the -//! top-level block of the given function. -//! -//! There is one exception to the above rule: `llvm.dbg.declare` instruction -//! must be linked to the source location of the variable being declared. For -//! function parameters these `llvm.dbg.declare` instructions typically occur -//! in the middle of the prologue, however, they are ignored by LLVM's prologue -//! detection. The `create_argument_metadata()` and related functions take care -//! of linking the `llvm.dbg.declare` instructions to the correct source -//! locations even while source location emission is still disabled, so there -//! is no need to do anything special with source location handling here. -//! -//! ## Unique Type Identification -//! -//! In order for link-time optimization to work properly, LLVM needs a unique -//! type identifier that tells it across compilation units which types are the -//! same as others. This type identifier is created by -//! TypeMap::get_unique_type_id_of_type() using the following algorithm: -//! -//! (1) Primitive types have their name as ID -//! (2) Structs, enums and traits have a multipart identifier -//! -//! (1) The first part is the SVH (strict version hash) of the crate they -//! were originally defined in -//! -//! (2) The second part is the ast::NodeId of the definition in their -//! original crate -//! -//! (3) The final part is a concatenation of the type IDs of their concrete -//! type arguments if they are generic types. -//! -//! (3) Tuple-, pointer and function types are structurally identified, which -//! means that they are equivalent if their component types are equivalent -//! (i.e. (i32, i32) is the same regardless in which crate it is used). -//! -//! This algorithm also provides a stable ID for types that are defined in one -//! crate but instantiated from metadata within another crate. We just have to -//! take care to always map crate and node IDs back to the original crate -//! context. -//! -//! As a side-effect these unique type IDs also help to solve a problem arising -//! from lifetime parameters. Since lifetime parameters are completely omitted -//! in debuginfo, more than one `Ty` instance may map to the same debuginfo -//! type metadata, that is, some struct `Struct<'a>` may have N instantiations -//! with different concrete substitutions for `'a`, and thus there will be N -//! `Ty` instances for the type `Struct<'a>` even though it is not generic -//! otherwise. Unfortunately this means that we cannot use `ty::type_id()` as -//! cheap identifier for type metadata---we have done this in the past, but it -//! led to unnecessary metadata duplication in the best case and LLVM -//! assertions in the worst. However, the unique type ID as described above -//! *can* be used as identifier. Since it is comparatively expensive to -//! construct, though, `ty::type_id()` is still used additionally as an -//! optimization for cases where the exact same type has been seen before -//! (which is most of the time). diff --git a/src/librustc_trans/debuginfo/gdb.rs b/src/librustc_trans/debuginfo/gdb.rs deleted file mode 100644 index 0b4858c7ab0..00000000000 --- a/src/librustc_trans/debuginfo/gdb.rs +++ /dev/null @@ -1,88 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// .debug_gdb_scripts binary section. - -use llvm; - -use common::{C_bytes, CodegenCx, C_i32}; -use builder::Builder; -use declare; -use type_::Type; -use rustc::session::config::NoDebugInfo; - -use std::ptr; -use syntax::attr; - - -/// Inserts a side-effect free instruction sequence that makes sure that the -/// .debug_gdb_scripts global is referenced, so it isn't removed by the linker. -pub fn insert_reference_to_gdb_debug_scripts_section_global(bx: &Builder) { - if needs_gdb_debug_scripts_section(bx.cx) { - let gdb_debug_scripts_section = get_or_insert_gdb_debug_scripts_section_global(bx.cx); - // Load just the first byte as that's all that's necessary to force - // LLVM to keep around the reference to the global. - let indices = [C_i32(bx.cx, 0), C_i32(bx.cx, 0)]; - let element = bx.inbounds_gep(gdb_debug_scripts_section, &indices); - let volative_load_instruction = bx.volatile_load(element); - unsafe { - llvm::LLVMSetAlignment(volative_load_instruction, 1); - } - } -} - -/// Allocates the global variable responsible for the .debug_gdb_scripts binary -/// section. -pub fn get_or_insert_gdb_debug_scripts_section_global(cx: &CodegenCx) - -> llvm::ValueRef { - let c_section_var_name = "__rustc_debug_gdb_scripts_section__\0"; - let section_var_name = &c_section_var_name[..c_section_var_name.len()-1]; - - let section_var = unsafe { - llvm::LLVMGetNamedGlobal(cx.llmod, - c_section_var_name.as_ptr() as *const _) - }; - - if section_var == ptr::null_mut() { - let section_name = b".debug_gdb_scripts\0"; - let section_contents = b"\x01gdb_load_rust_pretty_printers.py\0"; - - unsafe { - let llvm_type = Type::array(&Type::i8(cx), - section_contents.len() as u64); - - let section_var = declare::define_global(cx, section_var_name, - llvm_type).unwrap_or_else(||{ - bug!("symbol `{}` is already defined", section_var_name) - }); - llvm::LLVMSetSection(section_var, section_name.as_ptr() as *const _); - llvm::LLVMSetInitializer(section_var, C_bytes(cx, section_contents)); - llvm::LLVMSetGlobalConstant(section_var, llvm::True); - llvm::LLVMSetUnnamedAddr(section_var, llvm::True); - llvm::LLVMRustSetLinkage(section_var, llvm::Linkage::LinkOnceODRLinkage); - // This should make sure that the whole section is not larger than - // the string it contains. Otherwise we get a warning from GDB. - llvm::LLVMSetAlignment(section_var, 1); - section_var - } - } else { - section_var - } -} - -pub fn needs_gdb_debug_scripts_section(cx: &CodegenCx) -> bool { - let omit_gdb_pretty_printer_section = - attr::contains_name(&cx.tcx.hir.krate_attrs(), - "omit_gdb_pretty_printer_section"); - - !omit_gdb_pretty_printer_section && - cx.sess().opts.debuginfo != NoDebugInfo && - cx.sess().target.target.options.emit_debug_gdb_scripts -} diff --git a/src/librustc_trans/debuginfo/metadata.rs b/src/librustc_trans/debuginfo/metadata.rs deleted file mode 100644 index 4e77c0df65e..00000000000 --- a/src/librustc_trans/debuginfo/metadata.rs +++ /dev/null @@ -1,1773 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::RecursiveTypeDescription::*; -use self::MemberDescriptionFactory::*; -use self::EnumDiscriminantInfo::*; - -use super::utils::{debug_context, DIB, span_start, - get_namespace_for_item, create_DIArray, is_node_local_to_unit}; -use super::namespace::mangled_name_of_instance; -use super::type_names::compute_debuginfo_type_name; -use super::{CrateDebugContext}; -use abi; - -use llvm::{self, ValueRef}; -use llvm::debuginfo::{DIType, DIFile, DIScope, DIDescriptor, - DICompositeType, DILexicalBlock, DIFlags}; - -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def::CtorKind; -use rustc::hir::def_id::{DefId, CrateNum, LOCAL_CRATE}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::util::TypeIdHasher; -use rustc::ich::Fingerprint; -use rustc::ty::Instance; -use common::CodegenCx; -use rustc::ty::{self, AdtKind, ParamEnv, Ty, TyCtxt}; -use rustc::ty::layout::{self, Align, LayoutOf, PrimitiveExt, Size, TyLayout}; -use rustc::session::config; -use rustc::util::nodemap::FxHashMap; -use rustc::util::common::path2cstr; - -use libc::{c_uint, c_longlong}; -use std::ffi::CString; -use std::fmt::Write; -use std::ptr; -use std::path::{Path, PathBuf}; -use syntax::ast; -use syntax::symbol::{Interner, InternedString, Symbol}; -use syntax_pos::{self, Span, FileName}; - - -// From DWARF 5. -// See http://www.dwarfstd.org/ShowIssue.php?issue=140129.1 -const DW_LANG_RUST: c_uint = 0x1c; -#[allow(non_upper_case_globals)] -const DW_ATE_boolean: c_uint = 0x02; -#[allow(non_upper_case_globals)] -const DW_ATE_float: c_uint = 0x04; -#[allow(non_upper_case_globals)] -const DW_ATE_signed: c_uint = 0x05; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned: c_uint = 0x07; -#[allow(non_upper_case_globals)] -const DW_ATE_unsigned_char: c_uint = 0x08; - -pub const UNKNOWN_LINE_NUMBER: c_uint = 0; -pub const UNKNOWN_COLUMN_NUMBER: c_uint = 0; - -// ptr::null() doesn't work :( -pub const NO_SCOPE_METADATA: DIScope = (0 as DIScope); - -#[derive(Copy, Debug, Hash, Eq, PartialEq, Clone)] -pub struct UniqueTypeId(ast::Name); - -// The TypeMap is where the CrateDebugContext holds the type metadata nodes -// created so far. The metadata nodes are indexed by UniqueTypeId, and, for -// faster lookup, also by Ty. The TypeMap is responsible for creating -// UniqueTypeIds. -pub struct TypeMap<'tcx> { - // The UniqueTypeIds created so far - unique_id_interner: Interner, - // A map from UniqueTypeId to debuginfo metadata for that type. This is a 1:1 mapping. - unique_id_to_metadata: FxHashMap, - // A map from types to debuginfo metadata. This is a N:1 mapping. - type_to_metadata: FxHashMap, DIType>, - // A map from types to UniqueTypeId. This is a N:1 mapping. - type_to_unique_id: FxHashMap, UniqueTypeId> -} - -impl<'tcx> TypeMap<'tcx> { - pub fn new() -> TypeMap<'tcx> { - TypeMap { - unique_id_interner: Interner::new(), - type_to_metadata: FxHashMap(), - unique_id_to_metadata: FxHashMap(), - type_to_unique_id: FxHashMap(), - } - } - - // Adds a Ty to metadata mapping to the TypeMap. The method will fail if - // the mapping already exists. - fn register_type_with_metadata<'a>(&mut self, - type_: Ty<'tcx>, - metadata: DIType) { - if self.type_to_metadata.insert(type_, metadata).is_some() { - bug!("Type metadata for Ty '{}' is already in the TypeMap!", type_); - } - } - - // Adds a UniqueTypeId to metadata mapping to the TypeMap. The method will - // fail if the mapping already exists. - fn register_unique_id_with_metadata(&mut self, - unique_type_id: UniqueTypeId, - metadata: DIType) { - if self.unique_id_to_metadata.insert(unique_type_id, metadata).is_some() { - bug!("Type metadata for unique id '{}' is already in the TypeMap!", - self.get_unique_type_id_as_string(unique_type_id)); - } - } - - fn find_metadata_for_type(&self, type_: Ty<'tcx>) -> Option { - self.type_to_metadata.get(&type_).cloned() - } - - fn find_metadata_for_unique_id(&self, unique_type_id: UniqueTypeId) -> Option { - self.unique_id_to_metadata.get(&unique_type_id).cloned() - } - - // Get the string representation of a UniqueTypeId. This method will fail if - // the id is unknown. - fn get_unique_type_id_as_string(&self, unique_type_id: UniqueTypeId) -> &str { - let UniqueTypeId(interner_key) = unique_type_id; - self.unique_id_interner.get(interner_key) - } - - // Get the UniqueTypeId for the given type. If the UniqueTypeId for the given - // type has been requested before, this is just a table lookup. Otherwise an - // ID will be generated and stored for later lookup. - fn get_unique_type_id_of_type<'a>(&mut self, cx: &CodegenCx<'a, 'tcx>, - type_: Ty<'tcx>) -> UniqueTypeId { - // Let's see if we already have something in the cache - match self.type_to_unique_id.get(&type_).cloned() { - Some(unique_type_id) => return unique_type_id, - None => { /* generate one */} - }; - - // The hasher we are using to generate the UniqueTypeId. We want - // something that provides more than the 64 bits of the DefaultHasher. - let mut type_id_hasher = TypeIdHasher::::new(cx.tcx); - type_id_hasher.visit_ty(type_); - let unique_type_id = type_id_hasher.finish().to_hex(); - - let key = self.unique_id_interner.intern(&unique_type_id); - self.type_to_unique_id.insert(type_, UniqueTypeId(key)); - - return UniqueTypeId(key); - } - - // Get the UniqueTypeId for an enum variant. Enum variants are not really - // types of their own, so they need special handling. We still need a - // UniqueTypeId for them, since to debuginfo they *are* real types. - fn get_unique_type_id_of_enum_variant<'a>(&mut self, - cx: &CodegenCx<'a, 'tcx>, - enum_type: Ty<'tcx>, - variant_name: &str) - -> UniqueTypeId { - let enum_type_id = self.get_unique_type_id_of_type(cx, enum_type); - let enum_variant_type_id = format!("{}::{}", - self.get_unique_type_id_as_string(enum_type_id), - variant_name); - let interner_key = self.unique_id_interner.intern(&enum_variant_type_id); - UniqueTypeId(interner_key) - } -} - -// A description of some recursive type. It can either be already finished (as -// with FinalMetadata) or it is not yet finished, but contains all information -// needed to generate the missing parts of the description. See the -// documentation section on Recursive Types at the top of this file for more -// information. -enum RecursiveTypeDescription<'tcx> { - UnfinishedMetadata { - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - member_description_factory: MemberDescriptionFactory<'tcx>, - }, - FinalMetadata(DICompositeType) -} - -fn create_and_register_recursive_type_forward_declaration<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - unfinished_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - metadata_stub: DICompositeType, - member_description_factory: MemberDescriptionFactory<'tcx>) - -> RecursiveTypeDescription<'tcx> { - - // Insert the stub into the TypeMap in order to allow for recursive references - let mut type_map = debug_context(cx).type_map.borrow_mut(); - type_map.register_unique_id_with_metadata(unique_type_id, metadata_stub); - type_map.register_type_with_metadata(unfinished_type, metadata_stub); - - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - member_description_factory, - } -} - -impl<'tcx> RecursiveTypeDescription<'tcx> { - // Finishes up the description of the type in question (mostly by providing - // descriptions of the fields of the given type) and returns the final type - // metadata. - fn finalize<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> MetadataCreationResult { - match *self { - FinalMetadata(metadata) => MetadataCreationResult::new(metadata, false), - UnfinishedMetadata { - unfinished_type, - unique_type_id, - metadata_stub, - ref member_description_factory, - } => { - // Make sure that we have a forward declaration of the type in - // the TypeMap so that recursive references are possible. This - // will always be the case if the RecursiveTypeDescription has - // been properly created through the - // create_and_register_recursive_type_forward_declaration() - // function. - { - let type_map = debug_context(cx).type_map.borrow(); - if type_map.find_metadata_for_unique_id(unique_type_id).is_none() || - type_map.find_metadata_for_type(unfinished_type).is_none() { - bug!("Forward declaration of potentially recursive type \ - '{:?}' was not found in TypeMap!", - unfinished_type); - } - } - - // ... then create the member descriptions ... - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - // ... and attach them to the stub to complete it. - set_members_of_composite_type(cx, - metadata_stub, - &member_descriptions[..]); - return MetadataCreationResult::new(metadata_stub, true); - } - } - } -} - -// Returns from the enclosing function if the type metadata with the given -// unique id can be found in the type map -macro_rules! return_if_metadata_created_in_meantime { - ($cx: expr, $unique_type_id: expr) => ( - match debug_context($cx).type_map - .borrow() - .find_metadata_for_unique_id($unique_type_id) { - Some(metadata) => return MetadataCreationResult::new(metadata, true), - None => { /* proceed normally */ } - } - ) -} - -fn fixed_vec_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - unique_type_id: UniqueTypeId, - array_or_slice_type: Ty<'tcx>, - element_type: Ty<'tcx>, - span: Span) - -> MetadataCreationResult { - let element_type_metadata = type_metadata(cx, element_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let (size, align) = cx.size_and_align_of(array_or_slice_type); - - let upper_bound = match array_or_slice_type.sty { - ty::TyArray(_, len) => { - len.unwrap_usize(cx.tcx) as c_longlong - } - _ => -1 - }; - - let subrange = unsafe { - llvm::LLVMRustDIBuilderGetOrCreateSubrange(DIB(cx), 0, upper_bound) - }; - - let subscripts = create_DIArray(DIB(cx), &[subrange]); - let metadata = unsafe { - llvm::LLVMRustDIBuilderCreateArrayType( - DIB(cx), - size.bits(), - align.abi_bits() as u32, - element_type_metadata, - subscripts) - }; - - return MetadataCreationResult::new(metadata, false); -} - -fn vec_slice_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - slice_ptr_type: Ty<'tcx>, - element_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> MetadataCreationResult { - let data_ptr_type = cx.tcx.mk_imm_ptr(element_type); - - let data_ptr_metadata = type_metadata(cx, data_ptr_type, span); - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - let slice_type_name = compute_debuginfo_type_name(cx, slice_ptr_type, true); - - let (pointer_size, pointer_align) = cx.size_and_align_of(data_ptr_type); - let (usize_size, usize_align) = cx.size_and_align_of(cx.tcx.types.usize); - - let member_descriptions = [ - MemberDescription { - name: "data_ptr".to_string(), - type_metadata: data_ptr_metadata, - offset: Size::from_bytes(0), - size: pointer_size, - align: pointer_align, - flags: DIFlags::FlagZero, - }, - MemberDescription { - name: "length".to_string(), - type_metadata: type_metadata(cx, cx.tcx.types.usize, span), - offset: pointer_size, - size: usize_size, - align: usize_align, - flags: DIFlags::FlagZero, - }, - ]; - - let file_metadata = unknown_file_metadata(cx); - - let metadata = composite_type_metadata(cx, - slice_ptr_type, - &slice_type_name[..], - unique_type_id, - &member_descriptions, - NO_SCOPE_METADATA, - file_metadata, - span); - MetadataCreationResult::new(metadata, false) -} - -fn subroutine_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - unique_type_id: UniqueTypeId, - signature: ty::PolyFnSig<'tcx>, - span: Span) - -> MetadataCreationResult -{ - let signature = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &signature, - ); - - let mut signature_metadata: Vec = Vec::with_capacity(signature.inputs().len() + 1); - - // return type - signature_metadata.push(match signature.output().sty { - ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, signature.output(), span) - }); - - // regular arguments - for &argument_type in signature.inputs() { - signature_metadata.push(type_metadata(cx, argument_type, span)); - } - - return_if_metadata_created_in_meantime!(cx, unique_type_id); - - return MetadataCreationResult::new( - unsafe { - llvm::LLVMRustDIBuilderCreateSubroutineType( - DIB(cx), - unknown_file_metadata(cx), - create_DIArray(DIB(cx), &signature_metadata[..])) - }, - false); -} - -// FIXME(1563) This is all a bit of a hack because 'trait pointer' is an ill- -// defined concept. For the case of an actual trait pointer (i.e., Box, -// &Trait), trait_object_type should be the whole thing (e.g, Box) and -// trait_type should be the actual trait (e.g., Trait). Where the trait is part -// of a DST struct, there is no trait_object_type and the results of this -// function will be a little bit weird. -fn trait_pointer_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - trait_type: Ty<'tcx>, - trait_object_type: Option>, - unique_type_id: UniqueTypeId) - -> DIType { - // The implementation provided here is a stub. It makes sure that the trait - // type is assigned the correct name, size, namespace, and source location. - // But it does not describe the trait's methods. - - let containing_scope = match trait_type.sty { - ty::TyDynamic(ref data, ..) => if let Some(principal) = data.principal() { - let def_id = principal.def_id(); - get_namespace_for_item(cx, def_id) - } else { - NO_SCOPE_METADATA - }, - _ => { - bug!("debuginfo: Unexpected trait-object type in \ - trait_pointer_metadata(): {:?}", - trait_type); - } - }; - - let trait_object_type = trait_object_type.unwrap_or(trait_type); - let trait_type_name = - compute_debuginfo_type_name(cx, trait_object_type, false); - - let file_metadata = unknown_file_metadata(cx); - - let layout = cx.layout_of(cx.tcx.mk_mut_ptr(trait_type)); - - assert_eq!(abi::FAT_PTR_ADDR, 0); - assert_eq!(abi::FAT_PTR_EXTRA, 1); - - let data_ptr_field = layout.field(cx, 0); - let vtable_field = layout.field(cx, 1); - let member_descriptions = [ - MemberDescription { - name: "pointer".to_string(), - type_metadata: type_metadata(cx, - cx.tcx.mk_mut_ptr(cx.tcx.types.u8), - syntax_pos::DUMMY_SP), - offset: layout.fields.offset(0), - size: data_ptr_field.size, - align: data_ptr_field.align, - flags: DIFlags::FlagArtificial, - }, - MemberDescription { - name: "vtable".to_string(), - type_metadata: type_metadata(cx, vtable_field.ty, syntax_pos::DUMMY_SP), - offset: layout.fields.offset(1), - size: vtable_field.size, - align: vtable_field.align, - flags: DIFlags::FlagArtificial, - }, - ]; - - composite_type_metadata(cx, - trait_object_type, - &trait_type_name[..], - unique_type_id, - &member_descriptions, - containing_scope, - file_metadata, - syntax_pos::DUMMY_SP) -} - -pub fn type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - usage_site_span: Span) - -> DIType { - // Get the unique type id of this type. - let unique_type_id = { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - // First, try to find the type in TypeMap. If we have seen it before, we - // can exit early here. - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - return metadata; - }, - None => { - // The Ty is not in the TypeMap but maybe we have already seen - // an equivalent type (e.g. only differing in region arguments). - // In order to find out, generate the unique type id and look - // that up. - let unique_type_id = type_map.get_unique_type_id_of_type(cx, t); - match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => { - // There is already an equivalent type in the TypeMap. - // Register this Ty as an alias in the cache and - // return the cached metadata. - type_map.register_type_with_metadata(t, metadata); - return metadata; - }, - None => { - // There really is no type metadata for this type, so - // proceed by creating it. - unique_type_id - } - } - } - } - }; - - debug!("type_metadata: {:?}", t); - - let ptr_metadata = |ty: Ty<'tcx>| { - match ty.sty { - ty::TySlice(typ) => { - Ok(vec_slice_metadata(cx, t, typ, unique_type_id, usage_site_span)) - } - ty::TyStr => { - Ok(vec_slice_metadata(cx, t, cx.tcx.types.u8, unique_type_id, usage_site_span)) - } - ty::TyDynamic(..) => { - Ok(MetadataCreationResult::new( - trait_pointer_metadata(cx, ty, Some(t), unique_type_id), - false)) - } - _ => { - let pointee_metadata = type_metadata(cx, ty, usage_site_span); - - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return Err(metadata), - None => { /* proceed normally */ } - }; - - Ok(MetadataCreationResult::new(pointer_type_metadata(cx, t, pointee_metadata), - false)) - } - } - }; - - let MetadataCreationResult { metadata, already_stored_in_typemap } = match t.sty { - ty::TyNever | - ty::TyBool | - ty::TyChar | - ty::TyInt(_) | - ty::TyUint(_) | - ty::TyFloat(_) => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::TyTuple(ref elements) if elements.is_empty() => { - MetadataCreationResult::new(basic_type_metadata(cx, t), false) - } - ty::TyArray(typ, _) | - ty::TySlice(typ) => { - fixed_vec_metadata(cx, unique_type_id, t, typ, usage_site_span) - } - ty::TyStr => { - fixed_vec_metadata(cx, unique_type_id, t, cx.tcx.types.i8, usage_site_span) - } - ty::TyDynamic(..) => { - MetadataCreationResult::new( - trait_pointer_metadata(cx, t, None, unique_type_id), - false) - } - ty::TyForeign(..) => { - MetadataCreationResult::new( - foreign_type_metadata(cx, t, unique_type_id), - false) - } - ty::TyRawPtr(ty::TypeAndMut{ty, ..}) | - ty::TyRef(_, ty, _) => { - match ptr_metadata(ty) { - Ok(res) => res, - Err(metadata) => return metadata, - } - } - ty::TyAdt(def, _) if def.is_box() => { - match ptr_metadata(t.boxed_ty()) { - Ok(res) => res, - Err(metadata) => return metadata, - } - } - ty::TyFnDef(..) | ty::TyFnPtr(_) => { - let fn_metadata = subroutine_type_metadata(cx, - unique_type_id, - t.fn_sig(cx.tcx), - usage_site_span).metadata; - match debug_context(cx).type_map - .borrow() - .find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => return metadata, - None => { /* proceed normally */ } - }; - - // This is actually a function pointer, so wrap it in pointer DI - MetadataCreationResult::new(pointer_type_metadata(cx, t, fn_metadata), false) - - } - ty::TyClosure(def_id, substs) => { - let upvar_tys : Vec<_> = substs.upvar_tys(def_id, cx.tcx).collect(); - prepare_tuple_metadata(cx, - t, - &upvar_tys, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::TyGenerator(def_id, substs, _) => { - let upvar_tys : Vec<_> = substs.field_tys(def_id, cx.tcx).map(|t| { - cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), t) - }).collect(); - prepare_tuple_metadata(cx, - t, - &upvar_tys, - unique_type_id, - usage_site_span).finalize(cx) - } - ty::TyAdt(def, ..) => match def.adt_kind() { - AdtKind::Struct => { - prepare_struct_metadata(cx, - t, - unique_type_id, - usage_site_span).finalize(cx) - } - AdtKind::Union => { - prepare_union_metadata(cx, - t, - unique_type_id, - usage_site_span).finalize(cx) - } - AdtKind::Enum => { - prepare_enum_metadata(cx, - t, - def.did, - unique_type_id, - usage_site_span).finalize(cx) - } - }, - ty::TyTuple(ref elements) => { - prepare_tuple_metadata(cx, - t, - &elements[..], - unique_type_id, - usage_site_span).finalize(cx) - } - _ => { - bug!("debuginfo: unexpected type in type_metadata: {:?}", t) - } - }; - - { - let mut type_map = debug_context(cx).type_map.borrow_mut(); - - if already_stored_in_typemap { - // Also make sure that we already have a TypeMap entry for the unique type id. - let metadata_for_uid = match type_map.find_metadata_for_unique_id(unique_type_id) { - Some(metadata) => metadata, - None => { - span_bug!(usage_site_span, - "Expected type metadata for unique \ - type id '{}' to already be in \ - the debuginfo::TypeMap but it \ - was not. (Ty = {})", - type_map.get_unique_type_id_as_string(unique_type_id), - t); - } - }; - - match type_map.find_metadata_for_type(t) { - Some(metadata) => { - if metadata != metadata_for_uid { - span_bug!(usage_site_span, - "Mismatch between Ty and \ - UniqueTypeId maps in \ - debuginfo::TypeMap. \ - UniqueTypeId={}, Ty={}", - type_map.get_unique_type_id_as_string(unique_type_id), - t); - } - } - None => { - type_map.register_type_with_metadata(t, metadata); - } - } - } else { - type_map.register_type_with_metadata(t, metadata); - type_map.register_unique_id_with_metadata(unique_type_id, metadata); - } - } - - metadata -} - -pub fn file_metadata(cx: &CodegenCx, - file_name: &FileName, - defining_crate: CrateNum) -> DIFile { - debug!("file_metadata: file_name: {}, defining_crate: {}", - file_name, - defining_crate); - - let directory = if defining_crate == LOCAL_CRATE { - &cx.sess().working_dir.0 - } else { - // If the path comes from an upstream crate we assume it has been made - // independent of the compiler's working directory one way or another. - Path::new("") - }; - - file_metadata_raw(cx, &file_name.to_string(), &directory.to_string_lossy()) -} - -pub fn unknown_file_metadata(cx: &CodegenCx) -> DIFile { - file_metadata_raw(cx, "", "") -} - -fn file_metadata_raw(cx: &CodegenCx, - file_name: &str, - directory: &str) - -> DIFile { - let key = (Symbol::intern(file_name), Symbol::intern(directory)); - - if let Some(file_metadata) = debug_context(cx).created_files.borrow().get(&key) { - return *file_metadata; - } - - debug!("file_metadata: file_name: {}, directory: {}", file_name, directory); - - let file_name = CString::new(file_name).unwrap(); - let directory = CString::new(directory).unwrap(); - - let file_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFile(DIB(cx), - file_name.as_ptr(), - directory.as_ptr()) - }; - - let mut created_files = debug_context(cx).created_files.borrow_mut(); - created_files.insert(key, file_metadata); - file_metadata -} - -fn basic_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>) -> DIType { - - debug!("basic_type_metadata: {:?}", t); - - let (name, encoding) = match t.sty { - ty::TyNever => ("!", DW_ATE_unsigned), - ty::TyTuple(ref elements) if elements.is_empty() => - ("()", DW_ATE_unsigned), - ty::TyBool => ("bool", DW_ATE_boolean), - ty::TyChar => ("char", DW_ATE_unsigned_char), - ty::TyInt(int_ty) => { - (int_ty.ty_to_string(), DW_ATE_signed) - }, - ty::TyUint(uint_ty) => { - (uint_ty.ty_to_string(), DW_ATE_unsigned) - }, - ty::TyFloat(float_ty) => { - (float_ty.ty_to_string(), DW_ATE_float) - }, - _ => bug!("debuginfo::basic_type_metadata - t is invalid type") - }; - - let (size, align) = cx.size_and_align_of(t); - let name = CString::new(name).unwrap(); - let ty_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateBasicType( - DIB(cx), - name.as_ptr(), - size.bits(), - align.abi_bits() as u32, - encoding) - }; - - return ty_metadata; -} - -fn foreign_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - unique_type_id: UniqueTypeId) -> DIType { - debug!("foreign_type_metadata: {:?}", t); - - let name = compute_debuginfo_type_name(cx, t, false); - create_struct_stub(cx, t, &name, unique_type_id, NO_SCOPE_METADATA) -} - -fn pointer_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - pointer_type: Ty<'tcx>, - pointee_type_metadata: DIType) - -> DIType { - let (pointer_size, pointer_align) = cx.size_and_align_of(pointer_type); - let name = compute_debuginfo_type_name(cx, pointer_type, false); - let name = CString::new(name).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreatePointerType( - DIB(cx), - pointee_type_metadata, - pointer_size.bits(), - pointer_align.abi_bits() as u32, - name.as_ptr()) - } -} - -pub fn compile_unit_metadata(tcx: TyCtxt, - codegen_unit_name: &str, - debug_context: &CrateDebugContext) - -> DIDescriptor { - let mut name_in_debuginfo = match tcx.sess.local_crate_source_file { - Some(ref path) => path.clone(), - None => PathBuf::from(&*tcx.crate_name(LOCAL_CRATE).as_str()), - }; - - // The OSX linker has an idiosyncrasy where it will ignore some debuginfo - // if multiple object files with the same DW_AT_name are linked together. - // As a workaround we generate unique names for each object file. Those do - // not correspond to an actual source file but that should be harmless. - if tcx.sess.target.target.options.is_like_osx { - name_in_debuginfo.push("@"); - name_in_debuginfo.push(codegen_unit_name); - } - - debug!("compile_unit_metadata: {:?}", name_in_debuginfo); - // FIXME(#41252) Remove "clang LLVM" if we can get GDB and LLVM to play nice. - let producer = format!("clang LLVM (rustc version {})", - (option_env!("CFG_VERSION")).expect("CFG_VERSION")); - - let name_in_debuginfo = name_in_debuginfo.to_string_lossy().into_owned(); - let name_in_debuginfo = CString::new(name_in_debuginfo).unwrap(); - let work_dir = CString::new(&tcx.sess.working_dir.0.to_string_lossy()[..]).unwrap(); - let producer = CString::new(producer).unwrap(); - let flags = "\0"; - let split_name = "\0"; - - unsafe { - let file_metadata = llvm::LLVMRustDIBuilderCreateFile( - debug_context.builder, name_in_debuginfo.as_ptr(), work_dir.as_ptr()); - - let unit_metadata = llvm::LLVMRustDIBuilderCreateCompileUnit( - debug_context.builder, - DW_LANG_RUST, - file_metadata, - producer.as_ptr(), - tcx.sess.opts.optimize != config::OptLevel::No, - flags.as_ptr() as *const _, - 0, - split_name.as_ptr() as *const _); - - if tcx.sess.opts.debugging_opts.profile { - let cu_desc_metadata = llvm::LLVMRustMetadataAsValue(debug_context.llcontext, - unit_metadata); - - let gcov_cu_info = [ - path_to_mdstring(debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcno")), - path_to_mdstring(debug_context.llcontext, - &tcx.output_filenames(LOCAL_CRATE).with_extension("gcda")), - cu_desc_metadata, - ]; - let gcov_metadata = llvm::LLVMMDNodeInContext(debug_context.llcontext, - gcov_cu_info.as_ptr(), - gcov_cu_info.len() as c_uint); - - let llvm_gcov_ident = CString::new("llvm.gcov").unwrap(); - llvm::LLVMAddNamedMetadataOperand(debug_context.llmod, - llvm_gcov_ident.as_ptr(), - gcov_metadata); - } - - return unit_metadata; - }; - - fn path_to_mdstring(llcx: llvm::ContextRef, path: &Path) -> llvm::ValueRef { - let path_str = path2cstr(path); - unsafe { - llvm::LLVMMDStringInContext(llcx, - path_str.as_ptr(), - path_str.as_bytes().len() as c_uint) - } - } -} - -struct MetadataCreationResult { - metadata: DIType, - already_stored_in_typemap: bool -} - -impl MetadataCreationResult { - fn new(metadata: DIType, already_stored_in_typemap: bool) -> MetadataCreationResult { - MetadataCreationResult { - metadata, - already_stored_in_typemap, - } - } -} - -// Description of a type member, which can either be a regular field (as in -// structs or tuples) or an enum variant. -#[derive(Debug)] -struct MemberDescription { - name: String, - type_metadata: DIType, - offset: Size, - size: Size, - align: Align, - flags: DIFlags, -} - -// A factory for MemberDescriptions. It produces a list of member descriptions -// for some record-like type. MemberDescriptionFactories are used to defer the -// creation of type member descriptions in order to break cycles arising from -// recursive type definitions. -enum MemberDescriptionFactory<'tcx> { - StructMDF(StructMemberDescriptionFactory<'tcx>), - TupleMDF(TupleMemberDescriptionFactory<'tcx>), - EnumMDF(EnumMemberDescriptionFactory<'tcx>), - UnionMDF(UnionMemberDescriptionFactory<'tcx>), - VariantMDF(VariantMemberDescriptionFactory<'tcx>) -} - -impl<'tcx> MemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - match *self { - StructMDF(ref this) => { - this.create_member_descriptions(cx) - } - TupleMDF(ref this) => { - this.create_member_descriptions(cx) - } - EnumMDF(ref this) => { - this.create_member_descriptions(cx) - } - UnionMDF(ref this) => { - this.create_member_descriptions(cx) - } - VariantMDF(ref this) => { - this.create_member_descriptions(cx) - } - } - } -} - -//=----------------------------------------------------------------------------- -// Structs -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a struct -struct StructMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - variant: &'tcx ty::VariantDef, - span: Span, -} - -impl<'tcx> StructMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let layout = cx.layout_of(self.ty); - self.variant.fields.iter().enumerate().map(|(i, f)| { - let name = if self.variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - f.name.to_string() - }; - let field = layout.field(cx, i); - let (size, align) = field.size_and_align(); - MemberDescription { - name, - type_metadata: type_metadata(cx, field.ty, self.span), - offset: layout.fields.offset(i), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - - -fn prepare_struct_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - struct_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let struct_name = compute_debuginfo_type_name(cx, struct_type, false); - - let (struct_def_id, variant) = match struct_type.sty { - ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), - _ => bug!("prepare_struct_metadata on a non-ADT") - }; - - let containing_scope = get_namespace_for_item(cx, struct_def_id); - - let struct_metadata_stub = create_struct_stub(cx, - struct_type, - &struct_name, - unique_type_id, - containing_scope); - - create_and_register_recursive_type_forward_declaration( - cx, - struct_type, - unique_type_id, - struct_metadata_stub, - StructMDF(StructMemberDescriptionFactory { - ty: struct_type, - variant, - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Tuples -//=----------------------------------------------------------------------------- - -// Creates MemberDescriptions for the fields of a tuple -struct TupleMemberDescriptionFactory<'tcx> { - ty: Ty<'tcx>, - component_types: Vec>, - span: Span, -} - -impl<'tcx> TupleMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let layout = cx.layout_of(self.ty); - self.component_types.iter().enumerate().map(|(i, &component_type)| { - let (size, align) = cx.size_and_align_of(component_type); - MemberDescription { - name: format!("__{}", i), - type_metadata: type_metadata(cx, component_type, self.span), - offset: layout.fields.offset(i), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - -fn prepare_tuple_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - tuple_type: Ty<'tcx>, - component_types: &[Ty<'tcx>], - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let tuple_name = compute_debuginfo_type_name(cx, tuple_type, false); - - create_and_register_recursive_type_forward_declaration( - cx, - tuple_type, - unique_type_id, - create_struct_stub(cx, - tuple_type, - &tuple_name[..], - unique_type_id, - NO_SCOPE_METADATA), - TupleMDF(TupleMemberDescriptionFactory { - ty: tuple_type, - component_types: component_types.to_vec(), - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Unions -//=----------------------------------------------------------------------------- - -struct UnionMemberDescriptionFactory<'tcx> { - layout: TyLayout<'tcx>, - variant: &'tcx ty::VariantDef, - span: Span, -} - -impl<'tcx> UnionMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - self.variant.fields.iter().enumerate().map(|(i, f)| { - let field = self.layout.field(cx, i); - let (size, align) = field.size_and_align(); - MemberDescription { - name: f.name.to_string(), - type_metadata: type_metadata(cx, field.ty, self.span), - offset: Size::from_bytes(0), - size, - align, - flags: DIFlags::FlagZero, - } - }).collect() - } -} - -fn prepare_union_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - union_type: Ty<'tcx>, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let union_name = compute_debuginfo_type_name(cx, union_type, false); - - let (union_def_id, variant) = match union_type.sty { - ty::TyAdt(def, _) => (def.did, def.non_enum_variant()), - _ => bug!("prepare_union_metadata on a non-ADT") - }; - - let containing_scope = get_namespace_for_item(cx, union_def_id); - - let union_metadata_stub = create_union_stub(cx, - union_type, - &union_name, - unique_type_id, - containing_scope); - - create_and_register_recursive_type_forward_declaration( - cx, - union_type, - unique_type_id, - union_metadata_stub, - UnionMDF(UnionMemberDescriptionFactory { - layout: cx.layout_of(union_type), - variant, - span, - }) - ) -} - -//=----------------------------------------------------------------------------- -// Enums -//=----------------------------------------------------------------------------- - -// Describes the members of an enum value: An enum is described as a union of -// structs in DWARF. This MemberDescriptionFactory provides the description for -// the members of this union; so for every variant of the given enum, this -// factory will produce one MemberDescription (all with no name and a fixed -// offset of zero bytes). -struct EnumMemberDescriptionFactory<'tcx> { - enum_type: Ty<'tcx>, - layout: TyLayout<'tcx>, - discriminant_type_metadata: Option, - containing_scope: DIScope, - span: Span, -} - -impl<'tcx> EnumMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - let adt = &self.enum_type.ty_adt_def().unwrap(); - match self.layout.variants { - layout::Variants::Single { .. } if adt.variants.is_empty() => vec![], - layout::Variants::Single { index } => { - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, - self.layout, - &adt.variants[index], - NoDiscriminant, - self.containing_scope, - self.span); - - let member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &member_descriptions[..]); - vec![ - MemberDescription { - name: "".to_string(), - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: self.layout.size, - align: self.layout.align, - flags: DIFlags::FlagZero - } - ] - } - layout::Variants::Tagged { ref variants, .. } => { - let discriminant_info = RegularDiscriminant(self.discriminant_type_metadata - .expect("")); - (0..variants.len()).map(|i| { - let variant = self.layout.for_variant(cx, i); - let (variant_type_metadata, member_desc_factory) = - describe_enum_variant(cx, - variant, - &adt.variants[i], - discriminant_info, - self.containing_scope, - self.span); - - let member_descriptions = member_desc_factory - .create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &member_descriptions); - MemberDescription { - name: "".to_string(), - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero - } - }).collect() - } - layout::Variants::NicheFilling { dataful_variant, ref niche_variants, .. } => { - let variant = self.layout.for_variant(cx, dataful_variant); - // Create a description of the non-null variant - let (variant_type_metadata, member_description_factory) = - describe_enum_variant(cx, - variant, - &adt.variants[dataful_variant], - OptimizedDiscriminant, - self.containing_scope, - self.span); - - let variant_member_descriptions = - member_description_factory.create_member_descriptions(cx); - - set_members_of_composite_type(cx, - variant_type_metadata, - &variant_member_descriptions[..]); - - // Encode the information about the null variant in the union - // member's name. - let mut name = String::from("RUST$ENCODED$ENUM$"); - // HACK(eddyb) the debuggers should just handle offset+size - // of discriminant instead of us having to recover its path. - // Right now it's not even going to work for `niche_start > 0`, - // and for multiple niche variants it only supports the first. - fn compute_field_path<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &mut String, - layout: TyLayout<'tcx>, - offset: Size, - size: Size) { - for i in 0..layout.fields.count() { - let field_offset = layout.fields.offset(i); - if field_offset > offset { - continue; - } - let inner_offset = offset - field_offset; - let field = layout.field(cx, i); - if inner_offset + size <= field.size { - write!(name, "{}$", i).unwrap(); - compute_field_path(cx, name, field, inner_offset, size); - } - } - } - compute_field_path(cx, &mut name, - self.layout, - self.layout.fields.offset(0), - self.layout.field(cx, 0).size); - name.push_str(&adt.variants[*niche_variants.start()].name.as_str()); - - // Create the (singleton) list of descriptions of union members. - vec![ - MemberDescription { - name, - type_metadata: variant_type_metadata, - offset: Size::from_bytes(0), - size: variant.size, - align: variant.align, - flags: DIFlags::FlagZero - } - ] - } - } - } -} - -// Creates MemberDescriptions for the fields of a single enum variant. -struct VariantMemberDescriptionFactory<'tcx> { - // Cloned from the layout::Struct describing the variant. - offsets: Vec, - args: Vec<(String, Ty<'tcx>)>, - discriminant_type_metadata: Option, - span: Span, -} - -impl<'tcx> VariantMemberDescriptionFactory<'tcx> { - fn create_member_descriptions<'a>(&self, cx: &CodegenCx<'a, 'tcx>) - -> Vec { - self.args.iter().enumerate().map(|(i, &(ref name, ty))| { - let (size, align) = cx.size_and_align_of(ty); - MemberDescription { - name: name.to_string(), - type_metadata: match self.discriminant_type_metadata { - Some(metadata) if i == 0 => metadata, - _ => type_metadata(cx, ty, self.span) - }, - offset: self.offsets[i], - size, - align, - flags: DIFlags::FlagZero - } - }).collect() - } -} - -#[derive(Copy, Clone)] -enum EnumDiscriminantInfo { - RegularDiscriminant(DIType), - OptimizedDiscriminant, - NoDiscriminant -} - -// Returns a tuple of (1) type_metadata_stub of the variant, (2) the llvm_type -// of the variant, and (3) a MemberDescriptionFactory for producing the -// descriptions of the fields of the variant. This is a rudimentary version of a -// full RecursiveTypeDescription. -fn describe_enum_variant<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: layout::TyLayout<'tcx>, - variant: &'tcx ty::VariantDef, - discriminant_info: EnumDiscriminantInfo, - containing_scope: DIScope, - span: Span) - -> (DICompositeType, MemberDescriptionFactory<'tcx>) { - let variant_name = variant.name.as_str(); - let unique_type_id = debug_context(cx).type_map - .borrow_mut() - .get_unique_type_id_of_enum_variant( - cx, - layout.ty, - &variant_name); - - let metadata_stub = create_struct_stub(cx, - layout.ty, - &variant_name, - unique_type_id, - containing_scope); - - // If this is not a univariant enum, there is also the discriminant field. - let (discr_offset, discr_arg) = match discriminant_info { - RegularDiscriminant(_) => { - let enum_layout = cx.layout_of(layout.ty); - (Some(enum_layout.fields.offset(0)), - Some(("RUST$ENUM$DISR".to_string(), enum_layout.field(cx, 0).ty))) - } - _ => (None, None), - }; - let offsets = discr_offset.into_iter().chain((0..layout.fields.count()).map(|i| { - layout.fields.offset(i) - })).collect(); - - // Build an array of (field name, field type) pairs to be captured in the factory closure. - let args = discr_arg.into_iter().chain((0..layout.fields.count()).map(|i| { - let name = if variant.ctor_kind == CtorKind::Fn { - format!("__{}", i) - } else { - variant.fields[i].name.to_string() - }; - (name, layout.field(cx, i).ty) - })).collect(); - - let member_description_factory = - VariantMDF(VariantMemberDescriptionFactory { - offsets, - args, - discriminant_type_metadata: match discriminant_info { - RegularDiscriminant(discriminant_type_metadata) => { - Some(discriminant_type_metadata) - } - _ => None - }, - span, - }); - - (metadata_stub, member_description_factory) -} - -fn prepare_enum_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - enum_type: Ty<'tcx>, - enum_def_id: DefId, - unique_type_id: UniqueTypeId, - span: Span) - -> RecursiveTypeDescription<'tcx> { - let enum_name = compute_debuginfo_type_name(cx, enum_type, false); - - let containing_scope = get_namespace_for_item(cx, enum_def_id); - // FIXME: This should emit actual file metadata for the enum, but we - // currently can't get the necessary information when it comes to types - // imported from other crates. Formerly we violated the ODR when performing - // LTO because we emitted debuginfo for the same type with varying file - // metadata, so as a workaround we pretend that the type comes from - // - let file_metadata = unknown_file_metadata(cx); - - let def = enum_type.ty_adt_def().unwrap(); - let enumerators_metadata: Vec = def.discriminants(cx.tcx) - .zip(&def.variants) - .map(|(discr, v)| { - let token = v.name.as_str(); - let name = CString::new(token.as_bytes()).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreateEnumerator( - DIB(cx), - name.as_ptr(), - // FIXME: what if enumeration has i128 discriminant? - discr.val as u64) - } - }) - .collect(); - - let discriminant_type_metadata = |discr: layout::Primitive| { - let disr_type_key = (enum_def_id, discr); - let cached_discriminant_type_metadata = debug_context(cx).created_enum_disr_types - .borrow() - .get(&disr_type_key).cloned(); - match cached_discriminant_type_metadata { - Some(discriminant_type_metadata) => discriminant_type_metadata, - None => { - let (discriminant_size, discriminant_align) = - (discr.size(cx), discr.align(cx)); - let discriminant_base_type_metadata = - type_metadata(cx, discr.to_ty(cx.tcx), syntax_pos::DUMMY_SP); - let discriminant_name = get_enum_discriminant_name(cx, enum_def_id).as_str(); - - let name = CString::new(discriminant_name.as_bytes()).unwrap(); - let discriminant_type_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateEnumerationType( - DIB(cx), - containing_scope, - name.as_ptr(), - file_metadata, - UNKNOWN_LINE_NUMBER, - discriminant_size.bits(), - discriminant_align.abi_bits() as u32, - create_DIArray(DIB(cx), &enumerators_metadata), - discriminant_base_type_metadata) - }; - - debug_context(cx).created_enum_disr_types - .borrow_mut() - .insert(disr_type_key, discriminant_type_metadata); - - discriminant_type_metadata - } - } - }; - - let layout = cx.layout_of(enum_type); - - let discriminant_type_metadata = match layout.variants { - layout::Variants::Single { .. } | - layout::Variants::NicheFilling { .. } => None, - layout::Variants::Tagged { ref tag, .. } => { - Some(discriminant_type_metadata(tag.value)) - } - }; - - match (&layout.abi, discriminant_type_metadata) { - (&layout::Abi::Scalar(_), Some(discr)) => return FinalMetadata(discr), - _ => {} - } - - let (enum_type_size, enum_type_align) = layout.size_and_align(); - - let enum_name = CString::new(enum_name).unwrap(); - let unique_type_id_str = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let enum_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - enum_name.as_ptr(), - file_metadata, - UNKNOWN_LINE_NUMBER, - enum_type_size.bits(), - enum_type_align.abi_bits() as u32, - DIFlags::FlagZero, - ptr::null_mut(), - 0, // RuntimeLang - unique_type_id_str.as_ptr()) - }; - - return create_and_register_recursive_type_forward_declaration( - cx, - enum_type, - unique_type_id, - enum_metadata, - EnumMDF(EnumMemberDescriptionFactory { - enum_type, - layout, - discriminant_type_metadata, - containing_scope, - span, - }), - ); - - fn get_enum_discriminant_name(cx: &CodegenCx, - def_id: DefId) - -> InternedString { - cx.tcx.item_name(def_id) - } -} - -/// Creates debug information for a composite type, that is, anything that -/// results in a LLVM struct. -/// -/// Examples of Rust types to use this are: structs, tuples, boxes, vecs, and enums. -fn composite_type_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - composite_type: Ty<'tcx>, - composite_type_name: &str, - composite_type_unique_id: UniqueTypeId, - member_descriptions: &[MemberDescription], - containing_scope: DIScope, - - // Ignore source location information as long as it - // can't be reconstructed for non-local crates. - _file_metadata: DIFile, - _definition_span: Span) - -> DICompositeType { - // Create the (empty) struct metadata node ... - let composite_type_metadata = create_struct_stub(cx, - composite_type, - composite_type_name, - composite_type_unique_id, - containing_scope); - // ... and immediately create and add the member descriptions. - set_members_of_composite_type(cx, - composite_type_metadata, - member_descriptions); - - return composite_type_metadata; -} - -fn set_members_of_composite_type(cx: &CodegenCx, - composite_type_metadata: DICompositeType, - member_descriptions: &[MemberDescription]) { - // In some rare cases LLVM metadata uniquing would lead to an existing type - // description being used instead of a new one created in - // create_struct_stub. This would cause a hard to trace assertion in - // DICompositeType::SetTypeArray(). The following check makes sure that we - // get a better error message if this should happen again due to some - // regression. - { - let mut composite_types_completed = - debug_context(cx).composite_types_completed.borrow_mut(); - if composite_types_completed.contains(&composite_type_metadata) { - bug!("debuginfo::set_members_of_composite_type() - \ - Already completed forward declaration re-encountered."); - } else { - composite_types_completed.insert(composite_type_metadata); - } - } - - let member_metadata: Vec = member_descriptions - .iter() - .map(|member_description| { - let member_name = member_description.name.as_bytes(); - let member_name = CString::new(member_name).unwrap(); - unsafe { - llvm::LLVMRustDIBuilderCreateMemberType( - DIB(cx), - composite_type_metadata, - member_name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - member_description.size.bits(), - member_description.align.abi_bits() as u32, - member_description.offset.bits(), - member_description.flags, - member_description.type_metadata) - } - }) - .collect(); - - unsafe { - let type_array = create_DIArray(DIB(cx), &member_metadata[..]); - llvm::LLVMRustDICompositeTypeSetTypeArray( - DIB(cx), composite_type_metadata, type_array); - } -} - -// A convenience wrapper around LLVMRustDIBuilderCreateStructType(). Does not do -// any caching, does not add any fields to the struct. This can be done later -// with set_members_of_composite_type(). -fn create_struct_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - struct_type: Ty<'tcx>, - struct_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (struct_size, struct_align) = cx.size_and_align_of(struct_type); - - let name = CString::new(struct_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let metadata_stub = unsafe { - // LLVMRustDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - containing_scope, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - struct_size.bits(), - struct_align.abi_bits() as u32, - DIFlags::FlagZero, - ptr::null_mut(), - empty_array, - 0, - ptr::null_mut(), - unique_type_id.as_ptr()) - }; - - return metadata_stub; -} - -fn create_union_stub<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - union_type: Ty<'tcx>, - union_type_name: &str, - unique_type_id: UniqueTypeId, - containing_scope: DIScope) - -> DICompositeType { - let (union_size, union_align) = cx.size_and_align_of(union_type); - - let name = CString::new(union_type_name).unwrap(); - let unique_type_id = CString::new( - debug_context(cx).type_map.borrow().get_unique_type_id_as_string(unique_type_id).as_bytes() - ).unwrap(); - let metadata_stub = unsafe { - // LLVMRustDIBuilderCreateUnionType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - llvm::LLVMRustDIBuilderCreateUnionType( - DIB(cx), - containing_scope, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - union_size.bits(), - union_align.abi_bits() as u32, - DIFlags::FlagZero, - empty_array, - 0, // RuntimeLang - unique_type_id.as_ptr()) - }; - - return metadata_stub; -} - -/// Creates debug information for the given global variable. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_global_var_metadata(cx: &CodegenCx, - def_id: DefId, - global: ValueRef) { - if cx.dbg_cx.is_none() { - return; - } - - let tcx = cx.tcx; - let attrs = tcx.trans_fn_attrs(def_id); - - if attrs.flags.contains(TransFnAttrFlags::NO_DEBUG) { - return; - } - - let no_mangle = attrs.flags.contains(TransFnAttrFlags::NO_MANGLE); - // We may want to remove the namespace scope if we're in an extern block, see: - // https://github.com/rust-lang/rust/pull/46457#issuecomment-351750952 - let var_scope = get_namespace_for_item(cx, def_id); - let span = tcx.def_span(def_id); - - let (file_metadata, line_number) = if span != syntax_pos::DUMMY_SP { - let loc = span_start(cx, span); - (file_metadata(cx, &loc.file.name, LOCAL_CRATE), loc.line as c_uint) - } else { - (unknown_file_metadata(cx), UNKNOWN_LINE_NUMBER) - }; - - let is_local_to_unit = is_node_local_to_unit(cx, def_id); - let variable_type = Instance::mono(cx.tcx, def_id).ty(cx.tcx); - let type_metadata = type_metadata(cx, variable_type, span); - let var_name = tcx.item_name(def_id).to_string(); - let var_name = CString::new(var_name).unwrap(); - let linkage_name = if no_mangle { - None - } else { - let linkage_name = mangled_name_of_instance(cx, Instance::mono(tcx, def_id)); - Some(CString::new(linkage_name.to_string()).unwrap()) - }; - - let global_align = cx.align_of(variable_type); - - unsafe { - llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), - var_scope, - var_name.as_ptr(), - // If null, linkage_name field is omitted, - // which is what we want for no_mangle statics - linkage_name.as_ref() - .map_or(ptr::null(), |name| name.as_ptr()), - file_metadata, - line_number, - type_metadata, - is_local_to_unit, - global, - ptr::null_mut(), - global_align.abi() as u32, - ); - } -} - -// Creates an "extension" of an existing DIScope into another file. -pub fn extend_scope_to_file(cx: &CodegenCx, - scope_metadata: DIScope, - file: &syntax_pos::FileMap, - defining_crate: CrateNum) - -> DILexicalBlock { - let file_metadata = file_metadata(cx, &file.name, defining_crate); - unsafe { - llvm::LLVMRustDIBuilderCreateLexicalBlockFile( - DIB(cx), - scope_metadata, - file_metadata) - } -} - -/// Creates debug information for the given vtable, which is for the -/// given type. -/// -/// Adds the created metadata nodes directly to the crate's IR. -pub fn create_vtable_metadata<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: ty::Ty<'tcx>, - vtable: ValueRef) { - if cx.dbg_cx.is_none() { - return; - } - - let type_metadata = type_metadata(cx, ty, syntax_pos::DUMMY_SP); - - unsafe { - // LLVMRustDIBuilderCreateStructType() wants an empty array. A null - // pointer will lead to hard to trace and debug LLVM assertions - // later on in llvm/lib/IR/Value.cpp. - let empty_array = create_DIArray(DIB(cx), &[]); - - let name = CString::new("vtable").unwrap(); - - // Create a new one each time. We don't want metadata caching - // here, because each vtable will refer to a unique containing - // type. - let vtable_type = llvm::LLVMRustDIBuilderCreateStructType( - DIB(cx), - NO_SCOPE_METADATA, - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - Size::from_bytes(0).bits(), - cx.tcx.data_layout.pointer_align.abi_bits() as u32, - DIFlags::FlagArtificial, - ptr::null_mut(), - empty_array, - 0, - type_metadata, - name.as_ptr() - ); - - llvm::LLVMRustDIBuilderCreateStaticVariable(DIB(cx), - NO_SCOPE_METADATA, - name.as_ptr(), - // LLVM 3.9 - // doesn't accept - // null here, so - // pass the name - // as the linkage - // name. - name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER, - vtable_type, - true, - vtable, - ptr::null_mut(), - 0); - } -} diff --git a/src/librustc_trans/debuginfo/mod.rs b/src/librustc_trans/debuginfo/mod.rs deleted file mode 100644 index 2039a90a043..00000000000 --- a/src/librustc_trans/debuginfo/mod.rs +++ /dev/null @@ -1,542 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// See doc.rs for documentation. -mod doc; - -use self::VariableAccess::*; -use self::VariableKind::*; - -use self::utils::{DIB, span_start, create_DIArray, is_node_local_to_unit}; -use self::namespace::mangled_name_of_instance; -use self::type_names::compute_debuginfo_type_name; -use self::metadata::{type_metadata, file_metadata, TypeMap}; -use self::source_loc::InternalDebugLocation::{self, UnknownLocation}; - -use llvm; -use llvm::{ModuleRef, ContextRef, ValueRef}; -use llvm::debuginfo::{DIFile, DIType, DIScope, DIBuilderRef, DISubprogram, DIArray, DIFlags}; -use rustc::hir::TransFnAttrFlags; -use rustc::hir::def_id::{DefId, CrateNum}; -use rustc::ty::subst::{Substs, UnpackedKind}; - -use abi::Abi; -use common::CodegenCx; -use builder::Builder; -use monomorphize::Instance; -use rustc::ty::{self, ParamEnv, Ty, InstanceDef}; -use rustc::mir; -use rustc::session::config::{self, FullDebugInfo, LimitedDebugInfo, NoDebugInfo}; -use rustc::util::nodemap::{DefIdMap, FxHashMap, FxHashSet}; - -use libc::c_uint; -use std::cell::{Cell, RefCell}; -use std::ffi::CString; -use std::ptr; - -use syntax_pos::{self, Span, Pos}; -use syntax::ast; -use syntax::symbol::{Symbol, InternedString}; -use rustc::ty::layout::{self, LayoutOf}; - -pub mod gdb; -mod utils; -mod namespace; -mod type_names; -pub mod metadata; -mod create_scope_map; -mod source_loc; - -pub use self::create_scope_map::{create_mir_scopes, MirDebugScope}; -pub use self::source_loc::start_emitting_source_locations; -pub use self::metadata::create_global_var_metadata; -pub use self::metadata::create_vtable_metadata; -pub use self::metadata::extend_scope_to_file; -pub use self::source_loc::set_source_location; - -#[allow(non_upper_case_globals)] -const DW_TAG_auto_variable: c_uint = 0x100; -#[allow(non_upper_case_globals)] -const DW_TAG_arg_variable: c_uint = 0x101; - -/// A context object for maintaining all state needed by the debuginfo module. -pub struct CrateDebugContext<'tcx> { - llcontext: ContextRef, - llmod: ModuleRef, - builder: DIBuilderRef, - created_files: RefCell>, - created_enum_disr_types: RefCell>, - - type_map: RefCell>, - namespace_map: RefCell>, - - // This collection is used to assert that composite types (structs, enums, - // ...) have their members only set once: - composite_types_completed: RefCell>, -} - -impl<'tcx> CrateDebugContext<'tcx> { - pub fn new(llmod: ModuleRef) -> CrateDebugContext<'tcx> { - debug!("CrateDebugContext::new"); - let builder = unsafe { llvm::LLVMRustDIBuilderCreate(llmod) }; - // DIBuilder inherits context from the module, so we'd better use the same one - let llcontext = unsafe { llvm::LLVMGetModuleContext(llmod) }; - CrateDebugContext { - llcontext, - llmod, - builder, - created_files: RefCell::new(FxHashMap()), - created_enum_disr_types: RefCell::new(FxHashMap()), - type_map: RefCell::new(TypeMap::new()), - namespace_map: RefCell::new(DefIdMap()), - composite_types_completed: RefCell::new(FxHashSet()), - } - } -} - -pub enum FunctionDebugContext { - RegularContext(FunctionDebugContextData), - DebugInfoDisabled, - FunctionWithoutDebugInfo, -} - -impl FunctionDebugContext { - pub fn get_ref<'a>(&'a self, span: Span) -> &'a FunctionDebugContextData { - match *self { - FunctionDebugContext::RegularContext(ref data) => data, - FunctionDebugContext::DebugInfoDisabled => { - span_bug!(span, "{}", FunctionDebugContext::debuginfo_disabled_message()); - } - FunctionDebugContext::FunctionWithoutDebugInfo => { - span_bug!(span, "{}", FunctionDebugContext::should_be_ignored_message()); - } - } - } - - fn debuginfo_disabled_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext although debug info is disabled!" - } - - fn should_be_ignored_message() -> &'static str { - "debuginfo: Error trying to access FunctionDebugContext for function that should be \ - ignored by debug info!" - } -} - -pub struct FunctionDebugContextData { - fn_metadata: DISubprogram, - source_locations_enabled: Cell, - pub defining_crate: CrateNum, -} - -pub enum VariableAccess<'a> { - // The llptr given is an alloca containing the variable's value - DirectVariable { alloca: ValueRef }, - // The llptr given is an alloca containing the start of some pointer chain - // leading to the variable's content. - IndirectVariable { alloca: ValueRef, address_operations: &'a [i64] } -} - -pub enum VariableKind { - ArgumentVariable(usize /*index*/), - LocalVariable, - CapturedVariable, -} - -/// Create any deferred debug metadata nodes -pub fn finalize(cx: &CodegenCx) { - if cx.dbg_cx.is_none() { - return; - } - - debug!("finalize"); - - if gdb::needs_gdb_debug_scripts_section(cx) { - // Add a .debug_gdb_scripts section to this compile-unit. This will - // cause GDB to try and load the gdb_load_rust_pretty_printers.py file, - // which activates the Rust pretty printers for binary this section is - // contained in. - gdb::get_or_insert_gdb_debug_scripts_section_global(cx); - } - - unsafe { - llvm::LLVMRustDIBuilderFinalize(DIB(cx)); - llvm::LLVMRustDIBuilderDispose(DIB(cx)); - // Debuginfo generation in LLVM by default uses a higher - // version of dwarf than macOS currently understands. We can - // instruct LLVM to emit an older version of dwarf, however, - // for macOS to understand. For more info see #11352 - // This can be overridden using --llvm-opts -dwarf-version,N. - // Android has the same issue (#22398) - if cx.sess().target.target.options.is_like_osx || - cx.sess().target.target.options.is_like_android { - llvm::LLVMRustAddModuleFlag(cx.llmod, - "Dwarf Version\0".as_ptr() as *const _, - 2) - } - - // Indicate that we want CodeView debug information on MSVC - if cx.sess().target.target.options.is_like_msvc { - llvm::LLVMRustAddModuleFlag(cx.llmod, - "CodeView\0".as_ptr() as *const _, - 1) - } - - // Prevent bitcode readers from deleting the debug info. - let ptr = "Debug Info Version\0".as_ptr(); - llvm::LLVMRustAddModuleFlag(cx.llmod, ptr as *const _, - llvm::LLVMRustDebugMetadataVersion()); - }; -} - -/// Creates the function-specific debug context. -/// -/// Returns the FunctionDebugContext for the function which holds state needed -/// for debug info creation. The function may also return another variant of the -/// FunctionDebugContext enum which indicates why no debuginfo should be created -/// for the function. -pub fn create_function_debug_context<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, - sig: ty::FnSig<'tcx>, - llfn: ValueRef, - mir: &mir::Mir) -> FunctionDebugContext { - if cx.sess().opts.debuginfo == NoDebugInfo { - return FunctionDebugContext::DebugInfoDisabled; - } - - if let InstanceDef::Item(def_id) = instance.def { - if cx.tcx.trans_fn_attrs(def_id).flags.contains(TransFnAttrFlags::NO_DEBUG) { - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - } - - let span = mir.span; - - // This can be the case for functions inlined from another crate - if span == syntax_pos::DUMMY_SP { - // FIXME(simulacrum): Probably can't happen; remove. - return FunctionDebugContext::FunctionWithoutDebugInfo; - } - - let def_id = instance.def_id(); - let containing_scope = get_containing_scope(cx, instance); - let loc = span_start(cx, span); - let file_metadata = file_metadata(cx, &loc.file.name, def_id.krate); - - let function_type_metadata = unsafe { - let fn_signature = get_function_signature(cx, sig); - llvm::LLVMRustDIBuilderCreateSubroutineType(DIB(cx), file_metadata, fn_signature) - }; - - // Find the enclosing function, in case this is a closure. - let def_key = cx.tcx.def_key(def_id); - let mut name = def_key.disambiguated_data.data.to_string(); - - let enclosing_fn_def_id = cx.tcx.closure_base_def_id(def_id); - - // Get_template_parameters() will append a `<...>` clause to the function - // name if necessary. - let generics = cx.tcx.generics_of(enclosing_fn_def_id); - let substs = instance.substs.truncate_to(cx.tcx, generics); - let template_parameters = get_template_parameters(cx, - &generics, - substs, - file_metadata, - &mut name); - - // Get the linkage_name, which is just the symbol name - let linkage_name = mangled_name_of_instance(cx, instance); - - let scope_line = span_start(cx, span).line; - let is_local_to_unit = is_node_local_to_unit(cx, def_id); - - let function_name = CString::new(name).unwrap(); - let linkage_name = CString::new(linkage_name.to_string()).unwrap(); - - let mut flags = DIFlags::FlagPrototyped; - - let local_id = cx.tcx.hir.as_local_node_id(def_id); - match *cx.sess().entry_fn.borrow() { - Some((id, _, _)) => { - if local_id == Some(id) { - flags = flags | DIFlags::FlagMainSubprogram; - } - } - None => {} - }; - if cx.layout_of(sig.output()).abi == ty::layout::Abi::Uninhabited { - flags = flags | DIFlags::FlagNoReturn; - } - - let fn_metadata = unsafe { - llvm::LLVMRustDIBuilderCreateFunction( - DIB(cx), - containing_scope, - function_name.as_ptr(), - linkage_name.as_ptr(), - file_metadata, - loc.line as c_uint, - function_type_metadata, - is_local_to_unit, - true, - scope_line as c_uint, - flags, - cx.sess().opts.optimize != config::OptLevel::No, - llfn, - template_parameters, - ptr::null_mut()) - }; - - // Initialize fn debug context (including scope map and namespace map) - let fn_debug_context = FunctionDebugContextData { - fn_metadata, - source_locations_enabled: Cell::new(false), - defining_crate: def_id.krate, - }; - - return FunctionDebugContext::RegularContext(fn_debug_context); - - fn get_function_signature<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - sig: ty::FnSig<'tcx>) -> DIArray { - if cx.sess().opts.debuginfo == LimitedDebugInfo { - return create_DIArray(DIB(cx), &[]); - } - - let mut signature = Vec::with_capacity(sig.inputs().len() + 1); - - // Return type -- llvm::DIBuilder wants this at index 0 - signature.push(match sig.output().sty { - ty::TyTuple(ref tys) if tys.is_empty() => ptr::null_mut(), - _ => type_metadata(cx, sig.output(), syntax_pos::DUMMY_SP) - }); - - let inputs = if sig.abi == Abi::RustCall { - &sig.inputs()[..sig.inputs().len() - 1] - } else { - sig.inputs() - }; - - // Arguments types - if cx.sess().target.target.options.is_like_msvc { - // FIXME(#42800): - // There is a bug in MSDIA that leads to a crash when it encounters - // a fixed-size array of `u8` or something zero-sized in a - // function-type (see #40477). - // As a workaround, we replace those fixed-size arrays with a - // pointer-type. So a function `fn foo(a: u8, b: [u8; 4])` would - // appear as `fn foo(a: u8, b: *const u8)` in debuginfo, - // and a function `fn bar(x: [(); 7])` as `fn bar(x: *const ())`. - // This transformed type is wrong, but these function types are - // already inaccurate due to ABI adjustments (see #42800). - signature.extend(inputs.iter().map(|&t| { - let t = match t.sty { - ty::TyArray(ct, _) - if (ct == cx.tcx.types.u8) || cx.layout_of(ct).is_zst() => { - cx.tcx.mk_imm_ptr(ct) - } - _ => t - }; - type_metadata(cx, t, syntax_pos::DUMMY_SP) - })); - } else { - signature.extend(inputs.iter().map(|t| { - type_metadata(cx, t, syntax_pos::DUMMY_SP) - })); - } - - if sig.abi == Abi::RustCall && !sig.inputs().is_empty() { - if let ty::TyTuple(args) = sig.inputs()[sig.inputs().len() - 1].sty { - for &argument_type in args { - signature.push(type_metadata(cx, argument_type, syntax_pos::DUMMY_SP)); - } - } - } - - return create_DIArray(DIB(cx), &signature[..]); - } - - fn get_template_parameters<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - generics: &ty::Generics, - substs: &Substs<'tcx>, - file_metadata: DIFile, - name_to_append_suffix_to: &mut String) - -> DIArray - { - if substs.types().next().is_none() { - return create_DIArray(DIB(cx), &[]); - } - - name_to_append_suffix_to.push('<'); - for (i, actual_type) in substs.types().enumerate() { - if i != 0 { - name_to_append_suffix_to.push_str(","); - } - - let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), actual_type); - // Add actual type name to <...> clause of function name - let actual_type_name = compute_debuginfo_type_name(cx, - actual_type, - true); - name_to_append_suffix_to.push_str(&actual_type_name[..]); - } - name_to_append_suffix_to.push('>'); - - // Again, only create type information if full debuginfo is enabled - let template_params: Vec<_> = if cx.sess().opts.debuginfo == FullDebugInfo { - let names = get_parameter_names(cx, generics); - substs.iter().zip(names).filter_map(|(kind, name)| { - if let UnpackedKind::Type(ty) = kind.unpack() { - let actual_type = cx.tcx.normalize_erasing_regions(ParamEnv::reveal_all(), ty); - let actual_type_metadata = - type_metadata(cx, actual_type, syntax_pos::DUMMY_SP); - let name = CString::new(name.as_str().as_bytes()).unwrap(); - Some(unsafe { - llvm::LLVMRustDIBuilderCreateTemplateTypeParameter( - DIB(cx), - ptr::null_mut(), - name.as_ptr(), - actual_type_metadata, - file_metadata, - 0, - 0) - }) - } else { - None - } - }).collect() - } else { - vec![] - }; - - return create_DIArray(DIB(cx), &template_params[..]); - } - - fn get_parameter_names(cx: &CodegenCx, - generics: &ty::Generics) - -> Vec { - let mut names = generics.parent.map_or(vec![], |def_id| { - get_parameter_names(cx, cx.tcx.generics_of(def_id)) - }); - names.extend(generics.params.iter().map(|param| param.name)); - names - } - - fn get_containing_scope<'cx, 'tcx>(cx: &CodegenCx<'cx, 'tcx>, - instance: Instance<'tcx>) - -> DIScope { - // First, let's see if this is a method within an inherent impl. Because - // if yes, we want to make the result subroutine DIE a child of the - // subroutine's self-type. - let self_type = cx.tcx.impl_of_method(instance.def_id()).and_then(|impl_def_id| { - // If the method does *not* belong to a trait, proceed - if cx.tcx.trait_id_of_impl(impl_def_id).is_none() { - let impl_self_ty = cx.tcx.subst_and_normalize_erasing_regions( - instance.substs, - ty::ParamEnv::reveal_all(), - &cx.tcx.type_of(impl_def_id), - ); - - // Only "class" methods are generally understood by LLVM, - // so avoid methods on other types (e.g. `<*mut T>::null`). - match impl_self_ty.sty { - ty::TyAdt(def, ..) if !def.is_box() => { - Some(type_metadata(cx, impl_self_ty, syntax_pos::DUMMY_SP)) - } - _ => None - } - } else { - // For trait method impls we still use the "parallel namespace" - // strategy - None - } - }); - - self_type.unwrap_or_else(|| { - namespace::item_namespace(cx, DefId { - krate: instance.def_id().krate, - index: cx.tcx - .def_key(instance.def_id()) - .parent - .expect("get_containing_scope: missing parent?") - }) - }) - } -} - -pub fn declare_local<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - dbg_context: &FunctionDebugContext, - variable_name: ast::Name, - variable_type: Ty<'tcx>, - scope_metadata: DIScope, - variable_access: VariableAccess, - variable_kind: VariableKind, - span: Span) { - let cx = bx.cx; - - let file = span_start(cx, span).file; - let file_metadata = file_metadata(cx, - &file.name, - dbg_context.get_ref(span).defining_crate); - - let loc = span_start(cx, span); - let type_metadata = type_metadata(cx, variable_type, span); - - let (argument_index, dwarf_tag) = match variable_kind { - ArgumentVariable(index) => (index as c_uint, DW_TAG_arg_variable), - LocalVariable | - CapturedVariable => (0, DW_TAG_auto_variable) - }; - let align = cx.align_of(variable_type); - - let name = CString::new(variable_name.as_str().as_bytes()).unwrap(); - match (variable_access, &[][..]) { - (DirectVariable { alloca }, address_operations) | - (IndirectVariable {alloca, address_operations}, _) => { - let metadata = unsafe { - llvm::LLVMRustDIBuilderCreateVariable( - DIB(cx), - dwarf_tag, - scope_metadata, - name.as_ptr(), - file_metadata, - loc.line as c_uint, - type_metadata, - cx.sess().opts.optimize != config::OptLevel::No, - DIFlags::FlagZero, - argument_index, - align.abi() as u32, - ) - }; - source_loc::set_debug_location(bx, - InternalDebugLocation::new(scope_metadata, loc.line, loc.col.to_usize())); - unsafe { - let debug_loc = llvm::LLVMGetCurrentDebugLocation(bx.llbuilder); - let instr = llvm::LLVMRustDIBuilderInsertDeclareAtEnd( - DIB(cx), - alloca, - metadata, - address_operations.as_ptr(), - address_operations.len() as c_uint, - debug_loc, - bx.llbb()); - - llvm::LLVMSetInstDebugLocation(bx.llbuilder, instr); - } - } - } - - match variable_kind { - ArgumentVariable(_) | CapturedVariable => { - assert!(!dbg_context.get_ref(span).source_locations_enabled.get()); - source_loc::set_debug_location(bx, UnknownLocation); - } - _ => { /* nothing to do */ } - } -} diff --git a/src/librustc_trans/debuginfo/namespace.rs b/src/librustc_trans/debuginfo/namespace.rs deleted file mode 100644 index 51c45de9dc2..00000000000 --- a/src/librustc_trans/debuginfo/namespace.rs +++ /dev/null @@ -1,66 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Namespace Handling. - -use super::metadata::{unknown_file_metadata, UNKNOWN_LINE_NUMBER}; -use super::utils::{DIB, debug_context}; -use monomorphize::Instance; -use rustc::ty; - -use llvm; -use llvm::debuginfo::DIScope; -use rustc::hir::def_id::DefId; -use rustc::hir::map::DefPathData; -use common::CodegenCx; - -use std::ffi::CString; -use std::ptr; - -pub fn mangled_name_of_instance<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, -) -> ty::SymbolName { - let tcx = cx.tcx; - tcx.symbol_name(instance) -} - -pub fn item_namespace(cx: &CodegenCx, def_id: DefId) -> DIScope { - if let Some(&scope) = debug_context(cx).namespace_map.borrow().get(&def_id) { - return scope; - } - - let def_key = cx.tcx.def_key(def_id); - let parent_scope = def_key.parent.map_or(ptr::null_mut(), |parent| { - item_namespace(cx, DefId { - krate: def_id.krate, - index: parent - }) - }); - - let namespace_name = match def_key.disambiguated_data.data { - DefPathData::CrateRoot => cx.tcx.crate_name(def_id.krate).as_str(), - data => data.as_interned_str().as_str() - }; - - let namespace_name = CString::new(namespace_name.as_bytes()).unwrap(); - - let scope = unsafe { - llvm::LLVMRustDIBuilderCreateNameSpace( - DIB(cx), - parent_scope, - namespace_name.as_ptr(), - unknown_file_metadata(cx), - UNKNOWN_LINE_NUMBER) - }; - - debug_context(cx).namespace_map.borrow_mut().insert(def_id, scope); - scope -} diff --git a/src/librustc_trans/debuginfo/source_loc.rs b/src/librustc_trans/debuginfo/source_loc.rs deleted file mode 100644 index 7440296ce5d..00000000000 --- a/src/librustc_trans/debuginfo/source_loc.rs +++ /dev/null @@ -1,107 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use self::InternalDebugLocation::*; - -use super::utils::{debug_context, span_start}; -use super::metadata::UNKNOWN_COLUMN_NUMBER; -use super::FunctionDebugContext; - -use llvm; -use llvm::debuginfo::DIScope; -use builder::Builder; - -use libc::c_uint; -use std::ptr; -use syntax_pos::{Span, Pos}; - -/// Sets the current debug location at the beginning of the span. -/// -/// Maps to a call to llvm::LLVMSetCurrentDebugLocation(...). -pub fn set_source_location( - debug_context: &FunctionDebugContext, bx: &Builder, scope: DIScope, span: Span -) { - let function_debug_context = match *debug_context { - FunctionDebugContext::DebugInfoDisabled => return, - FunctionDebugContext::FunctionWithoutDebugInfo => { - set_debug_location(bx, UnknownLocation); - return; - } - FunctionDebugContext::RegularContext(ref data) => data - }; - - let dbg_loc = if function_debug_context.source_locations_enabled.get() { - debug!("set_source_location: {}", bx.sess().codemap().span_to_string(span)); - let loc = span_start(bx.cx, span); - InternalDebugLocation::new(scope, loc.line, loc.col.to_usize()) - } else { - UnknownLocation - }; - set_debug_location(bx, dbg_loc); -} - -/// Enables emitting source locations for the given functions. -/// -/// Since we don't want source locations to be emitted for the function prelude, -/// they are disabled when beginning to translate a new function. This functions -/// switches source location emitting on and must therefore be called before the -/// first real statement/expression of the function is translated. -pub fn start_emitting_source_locations(dbg_context: &FunctionDebugContext) { - match *dbg_context { - FunctionDebugContext::RegularContext(ref data) => { - data.source_locations_enabled.set(true) - }, - _ => { /* safe to ignore */ } - } -} - - -#[derive(Copy, Clone, PartialEq)] -pub enum InternalDebugLocation { - KnownLocation { scope: DIScope, line: usize, col: usize }, - UnknownLocation -} - -impl InternalDebugLocation { - pub fn new(scope: DIScope, line: usize, col: usize) -> InternalDebugLocation { - KnownLocation { - scope, - line, - col, - } - } -} - -pub fn set_debug_location(bx: &Builder, debug_location: InternalDebugLocation) { - let metadata_node = match debug_location { - KnownLocation { scope, line, .. } => { - // Always set the column to zero like Clang and GCC - let col = UNKNOWN_COLUMN_NUMBER; - debug!("setting debug location to {} {}", line, col); - - unsafe { - llvm::LLVMRustDIBuilderCreateDebugLocation( - debug_context(bx.cx).llcontext, - line as c_uint, - col as c_uint, - scope, - ptr::null_mut()) - } - } - UnknownLocation => { - debug!("clearing debug location "); - ptr::null_mut() - } - }; - - unsafe { - llvm::LLVMSetCurrentDebugLocation(bx.llbuilder, metadata_node); - } -} diff --git a/src/librustc_trans/debuginfo/type_names.rs b/src/librustc_trans/debuginfo/type_names.rs deleted file mode 100644 index 05a74db3a6c..00000000000 --- a/src/librustc_trans/debuginfo/type_names.rs +++ /dev/null @@ -1,224 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Type Names for Debug Info. - -use common::CodegenCx; -use rustc::hir::def_id::DefId; -use rustc::ty::subst::Substs; -use rustc::ty::{self, Ty}; - -use rustc::hir; - -// Compute the name of the type as it should be stored in debuginfo. Does not do -// any caching, i.e. calling the function twice with the same type will also do -// the work twice. The `qualified` parameter only affects the first level of the -// type name, further levels (i.e. type parameters) are always fully qualified. -pub fn compute_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool) - -> String { - let mut result = String::with_capacity(64); - push_debuginfo_type_name(cx, t, qualified, &mut result); - result -} - -// Pushes the name of the type as it should be stored in debuginfo on the -// `output` String. See also compute_debuginfo_type_name(). -pub fn push_debuginfo_type_name<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - t: Ty<'tcx>, - qualified: bool, - output: &mut String) { - // When targeting MSVC, emit C++ style type names for compatibility with - // .natvis visualizers (and perhaps other existing native debuggers?) - let cpp_like_names = cx.sess().target.target.options.is_like_msvc; - - match t.sty { - ty::TyBool => output.push_str("bool"), - ty::TyChar => output.push_str("char"), - ty::TyStr => output.push_str("str"), - ty::TyNever => output.push_str("!"), - ty::TyInt(int_ty) => output.push_str(int_ty.ty_to_string()), - ty::TyUint(uint_ty) => output.push_str(uint_ty.ty_to_string()), - ty::TyFloat(float_ty) => output.push_str(float_ty.ty_to_string()), - ty::TyForeign(def_id) => push_item_name(cx, def_id, qualified, output), - ty::TyAdt(def, substs) => { - push_item_name(cx, def.did, qualified, output); - push_type_params(cx, substs, output); - }, - ty::TyTuple(component_types) => { - output.push('('); - for &component_type in component_types { - push_debuginfo_type_name(cx, component_type, true, output); - output.push_str(", "); - } - if !component_types.is_empty() { - output.pop(); - output.pop(); - } - output.push(')'); - }, - ty::TyRawPtr(ty::TypeAndMut { ty: inner_type, mutbl } ) => { - if !cpp_like_names { - output.push('*'); - } - match mutbl { - hir::MutImmutable => output.push_str("const "), - hir::MutMutable => output.push_str("mut "), - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('*'); - } - }, - ty::TyRef(_, inner_type, mutbl) => { - if !cpp_like_names { - output.push('&'); - } - if mutbl == hir::MutMutable { - output.push_str("mut "); - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('*'); - } - }, - ty::TyArray(inner_type, len) => { - output.push('['); - push_debuginfo_type_name(cx, inner_type, true, output); - output.push_str(&format!("; {}", len.unwrap_usize(cx.tcx))); - output.push(']'); - }, - ty::TySlice(inner_type) => { - if cpp_like_names { - output.push_str("slice<"); - } else { - output.push('['); - } - - push_debuginfo_type_name(cx, inner_type, true, output); - - if cpp_like_names { - output.push('>'); - } else { - output.push(']'); - } - }, - ty::TyDynamic(ref trait_data, ..) => { - if let Some(principal) = trait_data.principal() { - let principal = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &principal, - ); - push_item_name(cx, principal.def_id, false, output); - push_type_params(cx, principal.substs, output); - } - }, - ty::TyFnDef(..) | ty::TyFnPtr(_) => { - let sig = t.fn_sig(cx.tcx); - if sig.unsafety() == hir::Unsafety::Unsafe { - output.push_str("unsafe "); - } - - let abi = sig.abi(); - if abi != ::abi::Abi::Rust { - output.push_str("extern \""); - output.push_str(abi.name()); - output.push_str("\" "); - } - - output.push_str("fn("); - - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - if !sig.inputs().is_empty() { - for ¶meter_type in sig.inputs() { - push_debuginfo_type_name(cx, parameter_type, true, output); - output.push_str(", "); - } - output.pop(); - output.pop(); - } - - if sig.variadic { - if !sig.inputs().is_empty() { - output.push_str(", ..."); - } else { - output.push_str("..."); - } - } - - output.push(')'); - - if !sig.output().is_nil() { - output.push_str(" -> "); - push_debuginfo_type_name(cx, sig.output(), true, output); - } - }, - ty::TyClosure(..) => { - output.push_str("closure"); - } - ty::TyGenerator(..) => { - output.push_str("generator"); - } - ty::TyError | - ty::TyInfer(_) | - ty::TyProjection(..) | - ty::TyAnon(..) | - ty::TyGeneratorWitness(..) | - ty::TyParam(_) => { - bug!("debuginfo: Trying to create type name for \ - unexpected type: {:?}", t); - } - } - - fn push_item_name(cx: &CodegenCx, - def_id: DefId, - qualified: bool, - output: &mut String) { - if qualified { - output.push_str(&cx.tcx.crate_name(def_id.krate).as_str()); - for path_element in cx.tcx.def_path(def_id).data { - output.push_str("::"); - output.push_str(&path_element.data.as_interned_str().as_str()); - } - } else { - output.push_str(&cx.tcx.item_name(def_id).as_str()); - } - } - - // Pushes the type parameters in the given `Substs` to the output string. - // This ignores region parameters, since they can't reliably be - // reconstructed for items from non-local crates. For local crates, this - // would be possible but with inlining and LTO we have to use the least - // common denominator - otherwise we would run into conflicts. - fn push_type_params<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - substs: &Substs<'tcx>, - output: &mut String) { - if substs.types().next().is_none() { - return; - } - - output.push('<'); - - for type_parameter in substs.types() { - push_debuginfo_type_name(cx, type_parameter, true, output); - output.push_str(", "); - } - - output.pop(); - output.pop(); - - output.push('>'); - } -} diff --git a/src/librustc_trans/debuginfo/utils.rs b/src/librustc_trans/debuginfo/utils.rs deleted file mode 100644 index 0a3f06b55f1..00000000000 --- a/src/librustc_trans/debuginfo/utils.rs +++ /dev/null @@ -1,65 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Utility Functions. - -use super::{CrateDebugContext}; -use super::namespace::item_namespace; - -use rustc::hir::def_id::DefId; -use rustc::ty::DefIdTree; - -use llvm; -use llvm::debuginfo::{DIScope, DIBuilderRef, DIDescriptor, DIArray}; -use common::{CodegenCx}; - -use syntax_pos::{self, Span}; - -pub fn is_node_local_to_unit(cx: &CodegenCx, def_id: DefId) -> bool -{ - // The is_local_to_unit flag indicates whether a function is local to the - // current compilation unit (i.e. if it is *static* in the C-sense). The - // *reachable* set should provide a good approximation of this, as it - // contains everything that might leak out of the current crate (by being - // externally visible or by being inlined into something externally - // visible). It might better to use the `exported_items` set from - // `driver::CrateAnalysis` in the future, but (atm) this set is not - // available in the translation pass. - !cx.tcx.is_reachable_non_generic(def_id) -} - -#[allow(non_snake_case)] -pub fn create_DIArray(builder: DIBuilderRef, arr: &[DIDescriptor]) -> DIArray { - return unsafe { - llvm::LLVMRustDIBuilderGetOrCreateArray(builder, arr.as_ptr(), arr.len() as u32) - }; -} - -/// Return syntax_pos::Loc corresponding to the beginning of the span -pub fn span_start(cx: &CodegenCx, span: Span) -> syntax_pos::Loc { - cx.sess().codemap().lookup_char_pos(span.lo()) -} - -#[inline] -pub fn debug_context<'a, 'tcx>(cx: &'a CodegenCx<'a, 'tcx>) - -> &'a CrateDebugContext<'tcx> { - cx.dbg_cx.as_ref().unwrap() -} - -#[inline] -#[allow(non_snake_case)] -pub fn DIB(cx: &CodegenCx) -> DIBuilderRef { - cx.dbg_cx.as_ref().unwrap().builder -} - -pub fn get_namespace_for_item(cx: &CodegenCx, def_id: DefId) -> DIScope { - item_namespace(cx, cx.tcx.parent(def_id) - .expect("get_namespace_for_item: missing parent?")) -} diff --git a/src/librustc_trans/declare.rs b/src/librustc_trans/declare.rs deleted file mode 100644 index 97721ffbf06..00000000000 --- a/src/librustc_trans/declare.rs +++ /dev/null @@ -1,223 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. -//! Declare various LLVM values. -//! -//! Prefer using functions and methods from this module rather than calling LLVM -//! functions directly. These functions do some additional work to ensure we do -//! the right thing given the preconceptions of trans. -//! -//! Some useful guidelines: -//! -//! * Use declare_* family of methods if you are declaring, but are not -//! interested in defining the ValueRef they return. -//! * Use define_* family of methods when you might be defining the ValueRef. -//! * When in doubt, define. - -use llvm::{self, ValueRef}; -use llvm::AttributePlace::Function; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::session::config::Sanitizer; -use rustc_target::spec::PanicStrategy; -use abi::{Abi, FnType, FnTypeExt}; -use attributes; -use context::CodegenCx; -use common; -use type_::Type; -use value::Value; - -use std::ffi::CString; - - -/// Declare a global value. -/// -/// If there’s a value with the same name already declared, the function will -/// return its ValueRef instead. -pub fn declare_global(cx: &CodegenCx, name: &str, ty: Type) -> llvm::ValueRef { - debug!("declare_global(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - unsafe { - llvm::LLVMRustGetOrInsertGlobal(cx.llmod, namebuf.as_ptr(), ty.to_ref()) - } -} - - -/// Declare a function. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -fn declare_raw_fn(cx: &CodegenCx, name: &str, callconv: llvm::CallConv, ty: Type) -> ValueRef { - debug!("declare_raw_fn(name={:?}, ty={:?})", name, ty); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - let llfn = unsafe { - llvm::LLVMRustGetOrInsertFunction(cx.llmod, namebuf.as_ptr(), ty.to_ref()) - }; - - llvm::SetFunctionCallConv(llfn, callconv); - // Function addresses in Rust are never significant, allowing functions to - // be merged. - llvm::SetUnnamedAddr(llfn, true); - - if cx.tcx.sess.opts.cg.no_redzone - .unwrap_or(cx.tcx.sess.target.target.options.disable_redzone) { - llvm::Attribute::NoRedZone.apply_llfn(Function, llfn); - } - - if let Some(ref sanitizer) = cx.tcx.sess.opts.debugging_opts.sanitizer { - match *sanitizer { - Sanitizer::Address => { - llvm::Attribute::SanitizeAddress.apply_llfn(Function, llfn); - }, - Sanitizer::Memory => { - llvm::Attribute::SanitizeMemory.apply_llfn(Function, llfn); - }, - Sanitizer::Thread => { - llvm::Attribute::SanitizeThread.apply_llfn(Function, llfn); - }, - _ => {} - } - } - - match cx.tcx.sess.opts.cg.opt_level.as_ref().map(String::as_ref) { - Some("s") => { - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - Some("z") => { - llvm::Attribute::MinSize.apply_llfn(Function, llfn); - llvm::Attribute::OptimizeForSize.apply_llfn(Function, llfn); - }, - _ => {}, - } - - if cx.tcx.sess.panic_strategy() != PanicStrategy::Unwind { - attributes::unwind(llfn, false); - } - - llfn -} - - -/// Declare a C ABI function. -/// -/// Only use this for foreign function ABIs and glue. For Rust functions use -/// `declare_fn` instead. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -pub fn declare_cfn(cx: &CodegenCx, name: &str, fn_type: Type) -> ValueRef { - declare_raw_fn(cx, name, llvm::CCallConv, fn_type) -} - - -/// Declare a Rust function. -/// -/// If there’s a value with the same name already declared, the function will -/// update the declaration and return existing ValueRef instead. -pub fn declare_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - debug!("declare_rust_fn(name={:?}, fn_type={:?})", name, fn_type); - let sig = common::ty_fn_sig(cx, fn_type); - let sig = cx.tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - debug!("declare_rust_fn (after region erasure) sig={:?}", sig); - - let fty = FnType::new(cx, sig, &[]); - let llfn = declare_raw_fn(cx, name, fty.llvm_cconv(), fty.llvm_type(cx)); - - if cx.layout_of(sig.output()).abi == layout::Abi::Uninhabited { - llvm::Attribute::NoReturn.apply_llfn(Function, llfn); - } - - if sig.abi != Abi::Rust && sig.abi != Abi::RustCall { - attributes::unwind(llfn, false); - } - - fty.apply_attrs_llfn(llfn); - - llfn -} - - -/// Declare a global with an intention to define it. -/// -/// Use this function when you intend to define a global. This function will -/// return None if the name already has a definition associated with it. In that -/// case an error should be reported to the user, because it usually happens due -/// to user’s fault (e.g. misuse of #[no_mangle] or #[export_name] attributes). -pub fn define_global(cx: &CodegenCx, name: &str, ty: Type) -> Option { - if get_defined_value(cx, name).is_some() { - None - } else { - Some(declare_global(cx, name, ty)) - } -} - -/// Declare a Rust function with an intention to define it. -/// -/// Use this function when you intend to define a function. This function will -/// return panic if the name already has a definition associated with it. This -/// can happen with #[no_mangle] or #[export_name], for example. -pub fn define_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - if get_defined_value(cx, name).is_some() { - cx.sess().fatal(&format!("symbol `{}` already defined", name)) - } else { - declare_fn(cx, name, fn_type) - } -} - -/// Declare a Rust function with an intention to define it. -/// -/// Use this function when you intend to define a function. This function will -/// return panic if the name already has a definition associated with it. This -/// can happen with #[no_mangle] or #[export_name], for example. -pub fn define_internal_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - fn_type: Ty<'tcx>) -> ValueRef { - let llfn = define_fn(cx, name, fn_type); - unsafe { llvm::LLVMRustSetLinkage(llfn, llvm::Linkage::InternalLinkage) }; - llfn -} - - -/// Get declared value by name. -pub fn get_declared_value(cx: &CodegenCx, name: &str) -> Option { - debug!("get_declared_value(name={:?})", name); - let namebuf = CString::new(name).unwrap_or_else(|_|{ - bug!("name {:?} contains an interior null byte", name) - }); - let val = unsafe { llvm::LLVMRustGetNamedValue(cx.llmod, namebuf.as_ptr()) }; - if val.is_null() { - debug!("get_declared_value: {:?} value is null", name); - None - } else { - debug!("get_declared_value: {:?} => {:?}", name, Value(val)); - Some(val) - } -} - -/// Get defined or externally defined (AvailableExternally linkage) value by -/// name. -pub fn get_defined_value(cx: &CodegenCx, name: &str) -> Option { - get_declared_value(cx, name).and_then(|val|{ - let declaration = unsafe { - llvm::LLVMIsDeclaration(val) != 0 - }; - if !declaration { - Some(val) - } else { - None - } - }) -} diff --git a/src/librustc_trans/diagnostics.rs b/src/librustc_trans/diagnostics.rs deleted file mode 100644 index 57cc33d09bb..00000000000 --- a/src/librustc_trans/diagnostics.rs +++ /dev/null @@ -1,55 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_snake_case)] - -register_long_diagnostics! { - -E0511: r##" -Invalid monomorphization of an intrinsic function was used. Erroneous code -example: - -```ignore (error-emitted-at-codegen-which-cannot-be-handled-by-compile_fail) -#![feature(platform_intrinsics)] - -extern "platform-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} - -fn main() { - unsafe { simd_add(0, 1); } - // error: invalid monomorphization of `simd_add` intrinsic -} -``` - -The generic type has to be a SIMD type. Example: - -``` -#![feature(repr_simd)] -#![feature(platform_intrinsics)] - -#[repr(simd)] -#[derive(Copy, Clone)] -struct i32x2(i32, i32); - -extern "platform-intrinsic" { - fn simd_add(a: T, b: T) -> T; -} - -unsafe { simd_add(i32x2(0, 0), i32x2(1, 2)); } // ok! -``` -"##, - -} - - -register_diagnostics! { - E0558 -} diff --git a/src/librustc_trans/glue.rs b/src/librustc_trans/glue.rs deleted file mode 100644 index c7275d09401..00000000000 --- a/src/librustc_trans/glue.rs +++ /dev/null @@ -1,122 +0,0 @@ -// Copyright 2012-2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! -// -// Code relating to drop glue. - -use std; - -use builder::Builder; -use common::*; -use llvm::{ValueRef}; -use llvm; -use meth; -use rustc::ty::layout::LayoutOf; -use rustc::ty::{self, Ty}; -use value::Value; - -pub fn size_and_align_of_dst<'a, 'tcx>(bx: &Builder<'a, 'tcx>, t: Ty<'tcx>, info: ValueRef) - -> (ValueRef, ValueRef) { - debug!("calculate size of DST: {}; with lost info: {:?}", - t, Value(info)); - if bx.cx.type_is_sized(t) { - let (size, align) = bx.cx.size_and_align_of(t); - debug!("size_and_align_of_dst t={} info={:?} size: {:?} align: {:?}", - t, Value(info), size, align); - let size = C_usize(bx.cx, size.bytes()); - let align = C_usize(bx.cx, align.abi()); - return (size, align); - } - assert!(!info.is_null()); - match t.sty { - ty::TyDynamic(..) => { - // load size/align from vtable - (meth::SIZE.get_usize(bx, info), meth::ALIGN.get_usize(bx, info)) - } - ty::TySlice(_) | ty::TyStr => { - let unit = t.sequence_element_type(bx.tcx()); - // The info in this case is the length of the str, so the size is that - // times the unit size. - let (size, align) = bx.cx.size_and_align_of(unit); - (bx.mul(info, C_usize(bx.cx, size.bytes())), - C_usize(bx.cx, align.abi())) - } - _ => { - let cx = bx.cx; - // First get the size of all statically known fields. - // Don't use size_of because it also rounds up to alignment, which we - // want to avoid, as the unsized field's alignment could be smaller. - assert!(!t.is_simd()); - let layout = cx.layout_of(t); - debug!("DST {} layout: {:?}", t, layout); - - let i = layout.fields.count() - 1; - let sized_size = layout.fields.offset(i).bytes(); - let sized_align = layout.align.abi(); - debug!("DST {} statically sized prefix size: {} align: {}", - t, sized_size, sized_align); - let sized_size = C_usize(cx, sized_size); - let sized_align = C_usize(cx, sized_align); - - // Recurse to get the size of the dynamically sized field (must be - // the last field). - let field_ty = layout.field(cx, i).ty; - let (unsized_size, mut unsized_align) = size_and_align_of_dst(bx, field_ty, info); - - // FIXME (#26403, #27023): We should be adding padding - // to `sized_size` (to accommodate the `unsized_align` - // required of the unsized field that follows) before - // summing it with `sized_size`. (Note that since #26403 - // is unfixed, we do not yet add the necessary padding - // here. But this is where the add would go.) - - // Return the sum of sizes and max of aligns. - let size = bx.add(sized_size, unsized_size); - - // Packed types ignore the alignment of their fields. - if let ty::TyAdt(def, _) = t.sty { - if def.repr.packed() { - unsized_align = sized_align; - } - } - - // Choose max of two known alignments (combined value must - // be aligned according to more restrictive of the two). - let align = match (const_to_opt_u128(sized_align, false), - const_to_opt_u128(unsized_align, false)) { - (Some(sized_align), Some(unsized_align)) => { - // If both alignments are constant, (the sized_align should always be), then - // pick the correct alignment statically. - C_usize(cx, std::cmp::max(sized_align, unsized_align) as u64) - } - _ => bx.select(bx.icmp(llvm::IntUGT, sized_align, unsized_align), - sized_align, - unsized_align) - }; - - // Issue #27023: must add any necessary padding to `size` - // (to make it a multiple of `align`) before returning it. - // - // Namely, the returned size should be, in C notation: - // - // `size + ((size & (align-1)) ? align : 0)` - // - // emulated via the semi-standard fast bit trick: - // - // `(size + (align-1)) & -align` - - let addend = bx.sub(align, C_usize(bx.cx, 1)); - let size = bx.and(bx.add(size, addend), bx.neg(align)); - - (size, align) - } - } -} diff --git a/src/librustc_trans/intrinsic.rs b/src/librustc_trans/intrinsic.rs deleted file mode 100644 index 86aa48b6a9e..00000000000 --- a/src/librustc_trans/intrinsic.rs +++ /dev/null @@ -1,1465 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use intrinsics::{self, Intrinsic}; -use llvm; -use llvm::{ValueRef}; -use abi::{Abi, FnType, LlvmType, PassMode}; -use mir::place::PlaceRef; -use mir::operand::{OperandRef, OperandValue}; -use base::*; -use common::*; -use declare; -use glue; -use type_::Type; -use type_of::LayoutLlvmExt; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{HasDataLayout, LayoutOf}; -use rustc::hir; -use syntax::ast; -use syntax::symbol::Symbol; -use builder::Builder; - -use rustc::session::Session; -use syntax_pos::Span; - -use std::cmp::Ordering; -use std::iter; - -fn get_simple_intrinsic(cx: &CodegenCx, name: &str) -> Option { - let llvm_name = match name { - "sqrtf32" => "llvm.sqrt.f32", - "sqrtf64" => "llvm.sqrt.f64", - "powif32" => "llvm.powi.f32", - "powif64" => "llvm.powi.f64", - "sinf32" => "llvm.sin.f32", - "sinf64" => "llvm.sin.f64", - "cosf32" => "llvm.cos.f32", - "cosf64" => "llvm.cos.f64", - "powf32" => "llvm.pow.f32", - "powf64" => "llvm.pow.f64", - "expf32" => "llvm.exp.f32", - "expf64" => "llvm.exp.f64", - "exp2f32" => "llvm.exp2.f32", - "exp2f64" => "llvm.exp2.f64", - "logf32" => "llvm.log.f32", - "logf64" => "llvm.log.f64", - "log10f32" => "llvm.log10.f32", - "log10f64" => "llvm.log10.f64", - "log2f32" => "llvm.log2.f32", - "log2f64" => "llvm.log2.f64", - "fmaf32" => "llvm.fma.f32", - "fmaf64" => "llvm.fma.f64", - "fabsf32" => "llvm.fabs.f32", - "fabsf64" => "llvm.fabs.f64", - "copysignf32" => "llvm.copysign.f32", - "copysignf64" => "llvm.copysign.f64", - "floorf32" => "llvm.floor.f32", - "floorf64" => "llvm.floor.f64", - "ceilf32" => "llvm.ceil.f32", - "ceilf64" => "llvm.ceil.f64", - "truncf32" => "llvm.trunc.f32", - "truncf64" => "llvm.trunc.f64", - "rintf32" => "llvm.rint.f32", - "rintf64" => "llvm.rint.f64", - "nearbyintf32" => "llvm.nearbyint.f32", - "nearbyintf64" => "llvm.nearbyint.f64", - "roundf32" => "llvm.round.f32", - "roundf64" => "llvm.round.f64", - "assume" => "llvm.assume", - "abort" => "llvm.trap", - _ => return None - }; - Some(cx.get_intrinsic(&llvm_name)) -} - -/// Remember to add all intrinsics here, in librustc_typeck/check/mod.rs, -/// and in libcore/intrinsics.rs; if you need access to any llvm intrinsics, -/// add them to librustc_trans/trans/context.rs -pub fn trans_intrinsic_call<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - callee_ty: Ty<'tcx>, - fn_ty: &FnType<'tcx, Ty<'tcx>>, - args: &[OperandRef<'tcx>], - llresult: ValueRef, - span: Span) { - let cx = bx.cx; - let tcx = cx.tcx; - - let (def_id, substs) = match callee_ty.sty { - ty::TyFnDef(def_id, substs) => (def_id, substs), - _ => bug!("expected fn item type, found {}", callee_ty) - }; - - let sig = callee_ty.fn_sig(tcx); - let sig = tcx.normalize_erasing_late_bound_regions(ty::ParamEnv::reveal_all(), &sig); - let arg_tys = sig.inputs(); - let ret_ty = sig.output(); - let name = &*tcx.item_name(def_id).as_str(); - - let llret_ty = cx.layout_of(ret_ty).llvm_type(cx); - let result = PlaceRef::new_sized(llresult, fn_ty.ret.layout, fn_ty.ret.layout.align); - - let simple = get_simple_intrinsic(cx, name); - let llval = match name { - _ if simple.is_some() => { - bx.call(simple.unwrap(), - &args.iter().map(|arg| arg.immediate()).collect::>(), - None) - } - "unreachable" => { - return; - }, - "likely" => { - let expect = cx.get_intrinsic(&("llvm.expect.i1")); - bx.call(expect, &[args[0].immediate(), C_bool(cx, true)], None) - } - "unlikely" => { - let expect = cx.get_intrinsic(&("llvm.expect.i1")); - bx.call(expect, &[args[0].immediate(), C_bool(cx, false)], None) - } - "try" => { - try_intrinsic(bx, cx, - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - llresult); - return; - } - "breakpoint" => { - let llfn = cx.get_intrinsic(&("llvm.debugtrap")); - bx.call(llfn, &[], None) - } - "size_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.size_of(tp_ty).bytes()) - } - "size_of_val" => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (llsize, _) = - glue::size_and_align_of_dst(bx, tp_ty, meta); - llsize - } else { - C_usize(cx, cx.size_of(tp_ty).bytes()) - } - } - "min_align_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.align_of(tp_ty).abi()) - } - "min_align_of_val" => { - let tp_ty = substs.type_at(0); - if let OperandValue::Pair(_, meta) = args[0].val { - let (_, llalign) = - glue::size_and_align_of_dst(bx, tp_ty, meta); - llalign - } else { - C_usize(cx, cx.align_of(tp_ty).abi()) - } - } - "pref_align_of" => { - let tp_ty = substs.type_at(0); - C_usize(cx, cx.align_of(tp_ty).pref()) - } - "type_name" => { - let tp_ty = substs.type_at(0); - let ty_name = Symbol::intern(&tp_ty.to_string()).as_str(); - C_str_slice(cx, ty_name) - } - "type_id" => { - C_u64(cx, cx.tcx.type_id_hash(substs.type_at(0))) - } - "init" => { - let ty = substs.type_at(0); - if !cx.layout_of(ty).is_zst() { - // Just zero out the stack slot. - // If we store a zero constant, LLVM will drown in vreg allocation for large data - // structures, and the generated code will be awful. (A telltale sign of this is - // large quantities of `mov [byte ptr foo],0` in the generated code.) - memset_intrinsic(bx, false, ty, llresult, C_u8(cx, 0), C_usize(cx, 1)); - } - return; - } - // Effectively no-ops - "uninit" => { - return; - } - "needs_drop" => { - let tp_ty = substs.type_at(0); - - C_bool(cx, bx.cx.type_needs_drop(tp_ty)) - } - "offset" => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - bx.inbounds_gep(ptr, &[offset]) - } - "arith_offset" => { - let ptr = args[0].immediate(); - let offset = args[1].immediate(); - bx.gep(ptr, &[offset]) - } - - "copy_nonoverlapping" => { - copy_intrinsic(bx, false, false, substs.type_at(0), - args[1].immediate(), args[0].immediate(), args[2].immediate()) - } - "copy" => { - copy_intrinsic(bx, true, false, substs.type_at(0), - args[1].immediate(), args[0].immediate(), args[2].immediate()) - } - "write_bytes" => { - memset_intrinsic(bx, false, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - - "volatile_copy_nonoverlapping_memory" => { - copy_intrinsic(bx, false, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_copy_memory" => { - copy_intrinsic(bx, true, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_set_memory" => { - memset_intrinsic(bx, true, substs.type_at(0), - args[0].immediate(), args[1].immediate(), args[2].immediate()) - } - "volatile_load" => { - let tp_ty = substs.type_at(0); - let mut ptr = args[0].immediate(); - if let PassMode::Cast(ty) = fn_ty.ret.mode { - ptr = bx.pointercast(ptr, ty.llvm_type(cx).ptr_to()); - } - let load = bx.volatile_load(ptr); - unsafe { - llvm::LLVMSetAlignment(load, cx.align_of(tp_ty).abi() as u32); - } - to_immediate(bx, load, cx.layout_of(tp_ty)) - }, - "volatile_store" => { - let dst = args[0].deref(bx.cx); - args[1].val.volatile_store(bx, dst); - return; - }, - "prefetch_read_data" | "prefetch_write_data" | - "prefetch_read_instruction" | "prefetch_write_instruction" => { - let expect = cx.get_intrinsic(&("llvm.prefetch")); - let (rw, cache_type) = match name { - "prefetch_read_data" => (0, 1), - "prefetch_write_data" => (1, 1), - "prefetch_read_instruction" => (0, 0), - "prefetch_write_instruction" => (1, 0), - _ => bug!() - }; - bx.call(expect, &[ - args[0].immediate(), - C_i32(cx, rw), - args[1].immediate(), - C_i32(cx, cache_type) - ], None) - }, - "ctlz" | "ctlz_nonzero" | "cttz" | "cttz_nonzero" | "ctpop" | "bswap" | - "bitreverse" | "add_with_overflow" | "sub_with_overflow" | - "mul_with_overflow" | "overflowing_add" | "overflowing_sub" | "overflowing_mul" | - "unchecked_div" | "unchecked_rem" | "unchecked_shl" | "unchecked_shr" | "exact_div" => { - let ty = arg_tys[0]; - match int_type_width_signed(ty, cx) { - Some((width, signed)) => - match name { - "ctlz" | "cttz" => { - let y = C_bool(bx.cx, false); - let llfn = cx.get_intrinsic(&format!("llvm.{}.i{}", name, width)); - bx.call(llfn, &[args[0].immediate(), y], None) - } - "ctlz_nonzero" | "cttz_nonzero" => { - let y = C_bool(bx.cx, true); - let llvm_name = &format!("llvm.{}.i{}", &name[..4], width); - let llfn = cx.get_intrinsic(llvm_name); - bx.call(llfn, &[args[0].immediate(), y], None) - } - "ctpop" => bx.call(cx.get_intrinsic(&format!("llvm.ctpop.i{}", width)), - &[args[0].immediate()], None), - "bswap" => { - if width == 8 { - args[0].immediate() // byte swap a u8/i8 is just a no-op - } else { - bx.call(cx.get_intrinsic(&format!("llvm.bswap.i{}", width)), - &[args[0].immediate()], None) - } - } - "bitreverse" => { - bx.call(cx.get_intrinsic(&format!("llvm.bitreverse.i{}", width)), - &[args[0].immediate()], None) - } - "add_with_overflow" | "sub_with_overflow" | "mul_with_overflow" => { - let intrinsic = format!("llvm.{}{}.with.overflow.i{}", - if signed { 's' } else { 'u' }, - &name[..3], width); - let llfn = bx.cx.get_intrinsic(&intrinsic); - - // Convert `i1` to a `bool`, and write it to the out parameter - let pair = bx.call(llfn, &[ - args[0].immediate(), - args[1].immediate() - ], None); - let val = bx.extract_value(pair, 0); - let overflow = bx.zext(bx.extract_value(pair, 1), Type::bool(cx)); - - let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); - let dest = result.project_field(bx, 1); - bx.store(overflow, dest.llval, dest.align); - - return; - }, - "overflowing_add" => bx.add(args[0].immediate(), args[1].immediate()), - "overflowing_sub" => bx.sub(args[0].immediate(), args[1].immediate()), - "overflowing_mul" => bx.mul(args[0].immediate(), args[1].immediate()), - "exact_div" => - if signed { - bx.exactsdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.exactudiv(args[0].immediate(), args[1].immediate()) - }, - "unchecked_div" => - if signed { - bx.sdiv(args[0].immediate(), args[1].immediate()) - } else { - bx.udiv(args[0].immediate(), args[1].immediate()) - }, - "unchecked_rem" => - if signed { - bx.srem(args[0].immediate(), args[1].immediate()) - } else { - bx.urem(args[0].immediate(), args[1].immediate()) - }, - "unchecked_shl" => bx.shl(args[0].immediate(), args[1].immediate()), - "unchecked_shr" => - if signed { - bx.ashr(args[0].immediate(), args[1].immediate()) - } else { - bx.lshr(args[0].immediate(), args[1].immediate()) - }, - _ => bug!(), - }, - None => { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, ty)); - return; - } - } - - }, - "fadd_fast" | "fsub_fast" | "fmul_fast" | "fdiv_fast" | "frem_fast" => { - let sty = &arg_tys[0].sty; - match float_type_width(sty) { - Some(_width) => - match name { - "fadd_fast" => bx.fadd_fast(args[0].immediate(), args[1].immediate()), - "fsub_fast" => bx.fsub_fast(args[0].immediate(), args[1].immediate()), - "fmul_fast" => bx.fmul_fast(args[0].immediate(), args[1].immediate()), - "fdiv_fast" => bx.fdiv_fast(args[0].immediate(), args[1].immediate()), - "frem_fast" => bx.frem_fast(args[0].immediate(), args[1].immediate()), - _ => bug!(), - }, - None => { - span_invalid_monomorphization_error( - tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic float type, found `{}`", name, sty)); - return; - } - } - - }, - - "discriminant_value" => { - args[0].deref(bx.cx).trans_get_discr(bx, ret_ty) - } - - "align_offset" => { - // `ptr as usize` - let ptr_val = bx.ptrtoint(args[0].immediate(), bx.cx.isize_ty); - // `ptr_val % align` - let align = args[1].immediate(); - let offset = bx.urem(ptr_val, align); - let zero = C_null(bx.cx.isize_ty); - // `offset == 0` - let is_zero = bx.icmp(llvm::IntPredicate::IntEQ, offset, zero); - // `if offset == 0 { 0 } else { align - offset }` - bx.select(is_zero, zero, bx.sub(align, offset)) - } - name if name.starts_with("simd_") => { - match generic_simd_intrinsic(bx, name, - callee_ty, - args, - ret_ty, llret_ty, - span) { - Ok(llval) => llval, - Err(()) => return - } - } - // This requires that atomic intrinsics follow a specific naming pattern: - // "atomic_[_]", and no ordering means SeqCst - name if name.starts_with("atomic_") => { - use llvm::AtomicOrdering::*; - - let split: Vec<&str> = name.split('_').collect(); - - let is_cxchg = split[1] == "cxchg" || split[1] == "cxchgweak"; - let (order, failorder) = match split.len() { - 2 => (SequentiallyConsistent, SequentiallyConsistent), - 3 => match split[2] { - "unordered" => (Unordered, Unordered), - "relaxed" => (Monotonic, Monotonic), - "acq" => (Acquire, Acquire), - "rel" => (Release, Monotonic), - "acqrel" => (AcquireRelease, Acquire), - "failrelaxed" if is_cxchg => - (SequentiallyConsistent, Monotonic), - "failacq" if is_cxchg => - (SequentiallyConsistent, Acquire), - _ => cx.sess().fatal("unknown ordering in atomic intrinsic") - }, - 4 => match (split[2], split[3]) { - ("acq", "failrelaxed") if is_cxchg => - (Acquire, Monotonic), - ("acqrel", "failrelaxed") if is_cxchg => - (AcquireRelease, Monotonic), - _ => cx.sess().fatal("unknown ordering in atomic intrinsic") - }, - _ => cx.sess().fatal("Atomic intrinsic not in correct format"), - }; - - let invalid_monomorphization = |ty| { - span_invalid_monomorphization_error(tcx.sess, span, - &format!("invalid monomorphization of `{}` intrinsic: \ - expected basic integer type, found `{}`", name, ty)); - }; - - match split[1] { - "cxchg" | "cxchgweak" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let weak = if split[1] == "cxchgweak" { llvm::True } else { llvm::False }; - let pair = bx.atomic_cmpxchg( - args[0].immediate(), - args[1].immediate(), - args[2].immediate(), - order, - failorder, - weak); - let val = bx.extract_value(pair, 0); - let success = bx.zext(bx.extract_value(pair, 1), Type::bool(bx.cx)); - - let dest = result.project_field(bx, 0); - bx.store(val, dest.llval, dest.align); - let dest = result.project_field(bx, 1); - bx.store(success, dest.llval, dest.align); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "load" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_load(args[0].immediate(), order, align) - } else { - return invalid_monomorphization(ty); - } - } - - "store" => { - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - let align = cx.align_of(ty); - bx.atomic_store(args[1].immediate(), args[0].immediate(), order, align); - return; - } else { - return invalid_monomorphization(ty); - } - } - - "fence" => { - bx.atomic_fence(order, llvm::SynchronizationScope::CrossThread); - return; - } - - "singlethreadfence" => { - bx.atomic_fence(order, llvm::SynchronizationScope::SingleThread); - return; - } - - // These are all AtomicRMW ops - op => { - let atom_op = match op { - "xchg" => llvm::AtomicXchg, - "xadd" => llvm::AtomicAdd, - "xsub" => llvm::AtomicSub, - "and" => llvm::AtomicAnd, - "nand" => llvm::AtomicNand, - "or" => llvm::AtomicOr, - "xor" => llvm::AtomicXor, - "max" => llvm::AtomicMax, - "min" => llvm::AtomicMin, - "umax" => llvm::AtomicUMax, - "umin" => llvm::AtomicUMin, - _ => cx.sess().fatal("unknown atomic operation") - }; - - let ty = substs.type_at(0); - if int_type_width_signed(ty, cx).is_some() { - bx.atomic_rmw(atom_op, args[0].immediate(), args[1].immediate(), order) - } else { - return invalid_monomorphization(ty); - } - } - } - } - - "nontemporal_store" => { - let dst = args[0].deref(bx.cx); - args[1].val.nontemporal_store(bx, dst); - return; - } - - _ => { - let intr = match Intrinsic::find(&name) { - Some(intr) => intr, - None => bug!("unknown intrinsic '{}'", name), - }; - fn one(x: Vec) -> T { - assert_eq!(x.len(), 1); - x.into_iter().next().unwrap() - } - fn ty_to_type(cx: &CodegenCx, t: &intrinsics::Type) -> Vec { - use intrinsics::Type::*; - match *t { - Void => vec![Type::void(cx)], - Integer(_signed, _width, llvm_width) => { - vec![Type::ix(cx, llvm_width as u64)] - } - Float(x) => { - match x { - 32 => vec![Type::f32(cx)], - 64 => vec![Type::f64(cx)], - _ => bug!() - } - } - Pointer(ref t, ref llvm_elem, _const) => { - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(cx, t)); - vec![elem.ptr_to()] - } - Vector(ref t, ref llvm_elem, length) => { - let t = llvm_elem.as_ref().unwrap_or(t); - let elem = one(ty_to_type(cx, t)); - vec![Type::vector(&elem, length as u64)] - } - Aggregate(false, ref contents) => { - let elems = contents.iter() - .map(|t| one(ty_to_type(cx, t))) - .collect::>(); - vec![Type::struct_(cx, &elems, false)] - } - Aggregate(true, ref contents) => { - contents.iter() - .flat_map(|t| ty_to_type(cx, t)) - .collect() - } - } - } - - // This allows an argument list like `foo, (bar, baz), - // qux` to be converted into `foo, bar, baz, qux`, integer - // arguments to be truncated as needed and pointers to be - // cast. - fn modify_as_needed<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - t: &intrinsics::Type, - arg: &OperandRef<'tcx>) - -> Vec - { - match *t { - intrinsics::Type::Aggregate(true, ref contents) => { - // We found a tuple that needs squishing! So - // run over the tuple and load each field. - // - // This assumes the type is "simple", i.e. no - // destructors, and the contents are SIMD - // etc. - assert!(!bx.cx.type_needs_drop(arg.layout.ty)); - let (ptr, align) = match arg.val { - OperandValue::Ref(ptr, align) => (ptr, align), - _ => bug!() - }; - let arg = PlaceRef::new_sized(ptr, arg.layout, align); - (0..contents.len()).map(|i| { - arg.project_field(bx, i).load(bx).immediate() - }).collect() - } - intrinsics::Type::Pointer(_, Some(ref llvm_elem), _) => { - let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); - vec![bx.pointercast(arg.immediate(), llvm_elem.ptr_to())] - } - intrinsics::Type::Vector(_, Some(ref llvm_elem), length) => { - let llvm_elem = one(ty_to_type(bx.cx, llvm_elem)); - vec![bx.bitcast(arg.immediate(), Type::vector(&llvm_elem, length as u64))] - } - intrinsics::Type::Integer(_, width, llvm_width) if width != llvm_width => { - // the LLVM intrinsic uses a smaller integer - // size than the C intrinsic's signature, so - // we have to trim it down here. - vec![bx.trunc(arg.immediate(), Type::ix(bx.cx, llvm_width as u64))] - } - _ => vec![arg.immediate()], - } - } - - - let inputs = intr.inputs.iter() - .flat_map(|t| ty_to_type(cx, t)) - .collect::>(); - - let outputs = one(ty_to_type(cx, &intr.output)); - - let llargs: Vec<_> = intr.inputs.iter().zip(args).flat_map(|(t, arg)| { - modify_as_needed(bx, t, arg) - }).collect(); - assert_eq!(inputs.len(), llargs.len()); - - let val = match intr.definition { - intrinsics::IntrinsicDef::Named(name) => { - let f = declare::declare_cfn(cx, - name, - Type::func(&inputs, &outputs)); - bx.call(f, &llargs, None) - } - }; - - match *intr.output { - intrinsics::Type::Aggregate(flatten, ref elems) => { - // the output is a tuple so we need to munge it properly - assert!(!flatten); - - for i in 0..elems.len() { - let dest = result.project_field(bx, i); - let val = bx.extract_value(val, i as u64); - bx.store(val, dest.llval, dest.align); - } - return; - } - _ => val, - } - } - }; - - if !fn_ty.ret.is_ignore() { - if let PassMode::Cast(ty) = fn_ty.ret.mode { - let ptr = bx.pointercast(result.llval, ty.llvm_type(cx).ptr_to()); - bx.store(llval, ptr, result.align); - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, result.layout) - .val.store(bx, result); - } - } -} - -fn copy_intrinsic<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - allow_overlap: bool, - volatile: bool, - ty: Ty<'tcx>, - dst: ValueRef, - src: ValueRef, - count: ValueRef) - -> ValueRef { - let cx = bx.cx; - let (size, align) = cx.size_and_align_of(ty); - let size = C_usize(cx, size.bytes()); - let align = C_i32(cx, align.abi() as i32); - - let operation = if allow_overlap { - "memmove" - } else { - "memcpy" - }; - - let name = format!("llvm.{}.p0i8.p0i8.i{}", operation, - cx.data_layout().pointer_size.bits()); - - let dst_ptr = bx.pointercast(dst, Type::i8p(cx)); - let src_ptr = bx.pointercast(src, Type::i8p(cx)); - let llfn = cx.get_intrinsic(&name); - - bx.call(llfn, - &[dst_ptr, - src_ptr, - bx.mul(size, count), - align, - C_bool(cx, volatile)], - None) -} - -fn memset_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - volatile: bool, - ty: Ty<'tcx>, - dst: ValueRef, - val: ValueRef, - count: ValueRef -) -> ValueRef { - let cx = bx.cx; - let (size, align) = cx.size_and_align_of(ty); - let size = C_usize(cx, size.bytes()); - let align = C_i32(cx, align.abi() as i32); - let dst = bx.pointercast(dst, Type::i8p(cx)); - call_memset(bx, dst, val, bx.mul(size, count), align, volatile) -} - -fn try_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef, -) { - if bx.sess().no_landing_pads() { - bx.call(func, &[data], None); - let ptr_align = bx.tcx().data_layout.pointer_align; - bx.store(C_null(Type::i8p(&bx.cx)), dest, ptr_align); - } else if wants_msvc_seh(bx.sess()) { - trans_msvc_try(bx, cx, func, data, local_ptr, dest); - } else { - trans_gnu_try(bx, cx, func, data, local_ptr, dest); - } -} - -// MSVC's definition of the `rust_try` function. -// -// This implementation uses the new exception handling instructions in LLVM -// which have support in LLVM for SEH on MSVC targets. Although these -// instructions are meant to work for all targets, as of the time of this -// writing, however, LLVM does not recommend the usage of these new instructions -// as the old ones are still more optimized. -fn trans_msvc_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef) { - let llfn = get_rust_try_fn(cx, &mut |bx| { - let cx = bx.cx; - - bx.set_personality_fn(bx.cx.eh_personality()); - - let normal = bx.build_sibling_block("normal"); - let catchswitch = bx.build_sibling_block("catchswitch"); - let catchpad = bx.build_sibling_block("catchpad"); - let caught = bx.build_sibling_block("caught"); - - let func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let local_ptr = llvm::get_param(bx.llfn(), 2); - - // We're generating an IR snippet that looks like: - // - // declare i32 @rust_try(%func, %data, %ptr) { - // %slot = alloca i64* - // invoke %func(%data) to label %normal unwind label %catchswitch - // - // normal: - // ret i32 0 - // - // catchswitch: - // %cs = catchswitch within none [%catchpad] unwind to caller - // - // catchpad: - // %tok = catchpad within %cs [%type_descriptor, 0, %slot] - // %ptr[0] = %slot[0] - // %ptr[1] = %slot[1] - // catchret from %tok to label %caught - // - // caught: - // ret i32 1 - // } - // - // This structure follows the basic usage of throw/try/catch in LLVM. - // For example, compile this C++ snippet to see what LLVM generates: - // - // #include - // - // int bar(void (*foo)(void), uint64_t *ret) { - // try { - // foo(); - // return 0; - // } catch(uint64_t a[2]) { - // ret[0] = a[0]; - // ret[1] = a[1]; - // return 1; - // } - // } - // - // More information can be found in libstd's seh.rs implementation. - let i64p = Type::i64(cx).ptr_to(); - let ptr_align = bx.tcx().data_layout.pointer_align; - let slot = bx.alloca(i64p, "slot", ptr_align); - bx.invoke(func, &[data], normal.llbb(), catchswitch.llbb(), - None); - - normal.ret(C_i32(cx, 0)); - - let cs = catchswitch.catch_switch(None, None, 1); - catchswitch.add_handler(cs, catchpad.llbb()); - - let tcx = cx.tcx; - let tydesc = match tcx.lang_items().msvc_try_filter() { - Some(did) => ::consts::get_static(cx, did), - None => bug!("msvc_try_filter not defined"), - }; - let tok = catchpad.catch_pad(cs, &[tydesc, C_i32(cx, 0), slot]); - let addr = catchpad.load(slot, ptr_align); - - let i64_align = bx.tcx().data_layout.i64_align; - let arg1 = catchpad.load(addr, i64_align); - let val1 = C_i32(cx, 1); - let arg2 = catchpad.load(catchpad.inbounds_gep(addr, &[val1]), i64_align); - let local_ptr = catchpad.bitcast(local_ptr, i64p); - catchpad.store(arg1, local_ptr, i64_align); - catchpad.store(arg2, catchpad.inbounds_gep(local_ptr, &[val1]), i64_align); - catchpad.catch_ret(tok, caught.llbb()); - - caught.ret(C_i32(cx, 1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[func, data, local_ptr], None); - let i32_align = bx.tcx().data_layout.i32_align; - bx.store(ret, dest, i32_align); -} - -// Definition of the standard "try" function for Rust using the GNU-like model -// of exceptions (e.g. the normal semantics of LLVM's landingpad and invoke -// instructions). -// -// This translation is a little surprising because we always call a shim -// function instead of inlining the call to `invoke` manually here. This is done -// because in LLVM we're only allowed to have one personality per function -// definition. The call to the `try` intrinsic is being inlined into the -// function calling it, and that function may already have other personality -// functions in play. By calling a shim we're guaranteed that our shim will have -// the right personality function. -fn trans_gnu_try<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - cx: &CodegenCx, - func: ValueRef, - data: ValueRef, - local_ptr: ValueRef, - dest: ValueRef) { - let llfn = get_rust_try_fn(cx, &mut |bx| { - let cx = bx.cx; - - // Translates the shims described above: - // - // bx: - // invoke %func(%args...) normal %normal unwind %catch - // - // normal: - // ret 0 - // - // catch: - // (ptr, _) = landingpad - // store ptr, %local_ptr - // ret 1 - // - // Note that the `local_ptr` data passed into the `try` intrinsic is - // expected to be `*mut *mut u8` for this to actually work, but that's - // managed by the standard library. - - let then = bx.build_sibling_block("then"); - let catch = bx.build_sibling_block("catch"); - - let func = llvm::get_param(bx.llfn(), 0); - let data = llvm::get_param(bx.llfn(), 1); - let local_ptr = llvm::get_param(bx.llfn(), 2); - bx.invoke(func, &[data], then.llbb(), catch.llbb(), None); - then.ret(C_i32(cx, 0)); - - // Type indicator for the exception being thrown. - // - // The first value in this tuple is a pointer to the exception object - // being thrown. The second value is a "selector" indicating which of - // the landing pad clauses the exception's type had been matched to. - // rust_try ignores the selector. - let lpad_ty = Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], - false); - let vals = catch.landing_pad(lpad_ty, bx.cx.eh_personality(), 1); - catch.add_clause(vals, C_null(Type::i8p(cx))); - let ptr = catch.extract_value(vals, 0); - let ptr_align = bx.tcx().data_layout.pointer_align; - catch.store(ptr, catch.bitcast(local_ptr, Type::i8p(cx).ptr_to()), ptr_align); - catch.ret(C_i32(cx, 1)); - }); - - // Note that no invoke is used here because by definition this function - // can't panic (that's what it's catching). - let ret = bx.call(llfn, &[func, data, local_ptr], None); - let i32_align = bx.tcx().data_layout.i32_align; - bx.store(ret, dest, i32_align); -} - -// Helper function to give a Block to a closure to translate a shim function. -// This is currently primarily used for the `try` intrinsic functions above. -fn gen_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - name: &str, - inputs: Vec>, - output: Ty<'tcx>, - trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) - -> ValueRef { - let rust_fn_ty = cx.tcx.mk_fn_ptr(ty::Binder::bind(cx.tcx.mk_fn_sig( - inputs.into_iter(), - output, - false, - hir::Unsafety::Unsafe, - Abi::Rust - ))); - let llfn = declare::define_internal_fn(cx, name, rust_fn_ty); - let bx = Builder::new_block(cx, llfn, "entry-block"); - trans(bx); - llfn -} - -// Helper function used to get a handle to the `__rust_try` function used to -// catch exceptions. -// -// This function is only generated once and is then cached. -fn get_rust_try_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - trans: &mut for<'b> FnMut(Builder<'b, 'tcx>)) - -> ValueRef { - if let Some(llfn) = cx.rust_try_fn.get() { - return llfn; - } - - // Define the type up front for the signature of the rust_try function. - let tcx = cx.tcx; - let i8p = tcx.mk_mut_ptr(tcx.types.i8); - let fn_ty = tcx.mk_fn_ptr(ty::Binder::bind(tcx.mk_fn_sig( - iter::once(i8p), - tcx.mk_nil(), - false, - hir::Unsafety::Unsafe, - Abi::Rust - ))); - let output = tcx.types.i32; - let rust_try = gen_fn(cx, "__rust_try", vec![fn_ty, i8p, i8p], output, trans); - cx.rust_try_fn.set(Some(rust_try)); - return rust_try -} - -fn span_invalid_monomorphization_error(a: &Session, b: Span, c: &str) { - span_err!(a, b, E0511, "{}", c); -} - -fn generic_simd_intrinsic<'a, 'tcx>( - bx: &Builder<'a, 'tcx>, - name: &str, - callee_ty: Ty<'tcx>, - args: &[OperandRef<'tcx>], - ret_ty: Ty<'tcx>, - llret_ty: Type, - span: Span -) -> Result { - // macros for error handling: - macro_rules! emit_error { - ($msg: tt) => { - emit_error!($msg, ) - }; - ($msg: tt, $($fmt: tt)*) => { - span_invalid_monomorphization_error( - bx.sess(), span, - &format!(concat!("invalid monomorphization of `{}` intrinsic: ", - $msg), - name, $($fmt)*)); - } - } - macro_rules! return_error { - ($($fmt: tt)*) => { - { - emit_error!($($fmt)*); - return Err(()); - } - } - } - - macro_rules! require { - ($cond: expr, $($fmt: tt)*) => { - if !$cond { - return_error!($($fmt)*); - } - }; - } - macro_rules! require_simd { - ($ty: expr, $position: expr) => { - require!($ty.is_simd(), "expected SIMD {} type, found non-SIMD `{}`", $position, $ty) - } - } - - - - let tcx = bx.tcx(); - let sig = tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &callee_ty.fn_sig(tcx), - ); - let arg_tys = sig.inputs(); - - // every intrinsic takes a SIMD vector as its first argument - require_simd!(arg_tys[0], "input"); - let in_ty = arg_tys[0]; - let in_elem = arg_tys[0].simd_type(tcx); - let in_len = arg_tys[0].simd_size(tcx); - - let comparison = match name { - "simd_eq" => Some(hir::BiEq), - "simd_ne" => Some(hir::BiNe), - "simd_lt" => Some(hir::BiLt), - "simd_le" => Some(hir::BiLe), - "simd_gt" => Some(hir::BiGt), - "simd_ge" => Some(hir::BiGe), - _ => None - }; - - if let Some(cmp_op) = comparison { - require_simd!(ret_ty, "return"); - - let out_len = ret_ty.simd_size(tcx); - require!(in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, in_ty, - ret_ty, out_len); - require!(llret_ty.element_type().kind() == llvm::Integer, - "expected return type with integer elements, found `{}` with non-integer `{}`", - ret_ty, - ret_ty.simd_type(tcx)); - - return Ok(compare_simd_types(bx, - args[0].immediate(), - args[1].immediate(), - in_elem, - llret_ty, - cmp_op)) - } - - if name.starts_with("simd_shuffle") { - let n: usize = match name["simd_shuffle".len()..].parse() { - Ok(n) => n, - Err(_) => span_bug!(span, - "bad `simd_shuffle` instruction only caught in trans?") - }; - - require_simd!(ret_ty, "return"); - - let out_len = ret_ty.simd_size(tcx); - require!(out_len == n, - "expected return type of length {}, found `{}` with length {}", - n, ret_ty, out_len); - require!(in_elem == ret_ty.simd_type(tcx), - "expected return element type `{}` (element of input `{}`), \ - found `{}` with element type `{}`", - in_elem, in_ty, - ret_ty, ret_ty.simd_type(tcx)); - - let total_len = in_len as u128 * 2; - - let vector = args[2].immediate(); - - let indices: Option> = (0..n) - .map(|i| { - let arg_idx = i; - let val = const_get_elt(vector, i as u64); - match const_to_opt_u128(val, true) { - None => { - emit_error!("shuffle index #{} is not a constant", arg_idx); - None - } - Some(idx) if idx >= total_len => { - emit_error!("shuffle index #{} is out of bounds (limit {})", - arg_idx, total_len); - None - } - Some(idx) => Some(C_i32(bx.cx, idx as i32)), - } - }) - .collect(); - let indices = match indices { - Some(i) => i, - None => return Ok(C_null(llret_ty)) - }; - - return Ok(bx.shuffle_vector(args[0].immediate(), - args[1].immediate(), - C_vector(&indices))) - } - - if name == "simd_insert" { - require!(in_elem == arg_tys[2], - "expected inserted type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, arg_tys[2]); - return Ok(bx.insert_element(args[0].immediate(), - args[2].immediate(), - args[1].immediate())) - } - if name == "simd_extract" { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return Ok(bx.extract_element(args[0].immediate(), args[1].immediate())) - } - - if name == "simd_select" { - let m_elem_ty = in_elem; - let m_len = in_len; - let v_len = arg_tys[1].simd_size(tcx); - require!(m_len == v_len, - "mismatched lengths: mask length `{}` != other vector length `{}`", - m_len, v_len - ); - match m_elem_ty.sty { - ty::TyInt(_) => {}, - _ => { - return_error!("mask element type is `{}`, expected `i_`", m_elem_ty); - } - } - // truncate the mask to a vector of i1s - let i1 = Type::i1(bx.cx); - let i1xn = Type::vector(&i1, m_len as u64); - let m_i1s = bx.trunc(args[0].immediate(), i1xn); - return Ok(bx.select(m_i1s, args[1].immediate(), args[2].immediate())); - } - - macro_rules! arith_red { - ($name:tt : $integer_reduce:ident, $float_reduce:ident, $ordered:expr) => { - if name == $name { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => { - let r = bx.$integer_reduce(args[0].immediate()); - if $ordered { - // if overflow occurs, the result is the - // mathematical result modulo 2^n: - if name.contains("mul") { - Ok(bx.mul(args[1].immediate(), r)) - } else { - Ok(bx.add(args[1].immediate(), r)) - } - } else { - Ok(bx.$integer_reduce(args[0].immediate())) - } - }, - ty::TyFloat(f) => { - // ordered arithmetic reductions take an accumulator - let acc = if $ordered { - let acc = args[1].immediate(); - // FIXME: https://bugs.llvm.org/show_bug.cgi?id=36734 - // * if the accumulator of the fadd isn't 0, incorrect - // code is generated - // * if the accumulator of the fmul isn't 1, incorrect - // code is generated - match const_get_real(acc) { - None => return_error!("accumulator of {} is not a constant", $name), - Some((v, loses_info)) => { - if $name.contains("mul") && v != 1.0_f64 { - return_error!("accumulator of {} is not 1.0", $name); - } else if $name.contains("add") && v != 0.0_f64 { - return_error!("accumulator of {} is not 0.0", $name); - } else if loses_info { - return_error!("accumulator of {} loses information", $name); - } - } - } - acc - } else { - // unordered arithmetic reductions do not: - match f.bit_width() { - 32 => C_undef(Type::f32(bx.cx)), - 64 => C_undef(Type::f64(bx.cx)), - v => { - return_error!(r#" -unsupported {} from `{}` with element `{}` of size `{}` to `{}`"#, - $name, in_ty, in_elem, v, ret_ty - ) - } - } - - }; - Ok(bx.$float_reduce(acc, args[0].immediate())) - } - _ => { - return_error!( - "unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty - ) - }, - } - } - } - } - - arith_red!("simd_reduce_add_ordered": vector_reduce_add, vector_reduce_fadd_fast, true); - arith_red!("simd_reduce_mul_ordered": vector_reduce_mul, vector_reduce_fmul_fast, true); - arith_red!("simd_reduce_add_unordered": vector_reduce_add, vector_reduce_fadd_fast, false); - arith_red!("simd_reduce_mul_unordered": vector_reduce_mul, vector_reduce_fmul_fast, false); - - macro_rules! minmax_red { - ($name:tt: $int_red:ident, $float_red:ident) => { - if name == $name { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - return match in_elem.sty { - ty::TyInt(_i) => { - Ok(bx.$int_red(args[0].immediate(), true)) - }, - ty::TyUint(_u) => { - Ok(bx.$int_red(args[0].immediate(), false)) - }, - ty::TyFloat(_f) => { - Ok(bx.$float_red(args[0].immediate())) - } - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - }, - } - } - - } - } - - minmax_red!("simd_reduce_min": vector_reduce_min, vector_reduce_fmin); - minmax_red!("simd_reduce_max": vector_reduce_max, vector_reduce_fmax); - - minmax_red!("simd_reduce_min_nanless": vector_reduce_min, vector_reduce_fmin_fast); - minmax_red!("simd_reduce_max_nanless": vector_reduce_max, vector_reduce_fmax_fast); - - macro_rules! bitwise_red { - ($name:tt : $red:ident, $boolean:expr) => { - if name == $name { - let input = if !$boolean { - require!(ret_ty == in_elem, - "expected return type `{}` (element of input `{}`), found `{}`", - in_elem, in_ty, ret_ty); - args[0].immediate() - } else { - match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => {}, - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - } - } - - // boolean reductions operate on vectors of i1s: - let i1 = Type::i1(bx.cx); - let i1xn = Type::vector(&i1, in_len as u64); - bx.trunc(args[0].immediate(), i1xn) - }; - return match in_elem.sty { - ty::TyInt(_) | ty::TyUint(_) => { - let r = bx.$red(input); - Ok( - if !$boolean { - r - } else { - bx.zext(r, Type::bool(bx.cx)) - } - ) - }, - _ => { - return_error!("unsupported {} from `{}` with element `{}` to `{}`", - $name, in_ty, in_elem, ret_ty) - }, - } - } - } - } - - bitwise_red!("simd_reduce_and": vector_reduce_and, false); - bitwise_red!("simd_reduce_or": vector_reduce_or, false); - bitwise_red!("simd_reduce_xor": vector_reduce_xor, false); - bitwise_red!("simd_reduce_all": vector_reduce_and, true); - bitwise_red!("simd_reduce_any": vector_reduce_or, true); - - if name == "simd_cast" { - require_simd!(ret_ty, "return"); - let out_len = ret_ty.simd_size(tcx); - require!(in_len == out_len, - "expected return type with length {} (same as input type `{}`), \ - found `{}` with length {}", - in_len, in_ty, - ret_ty, out_len); - // casting cares about nominal type, not just structural type - let out_elem = ret_ty.simd_type(tcx); - - if in_elem == out_elem { return Ok(args[0].immediate()); } - - enum Style { Float, Int(/* is signed? */ bool), Unsupported } - - let (in_style, in_width) = match in_elem.sty { - // vectors of pointer-sized integers should've been - // disallowed before here, so this unwrap is safe. - ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::TyFloat(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0) - }; - let (out_style, out_width) = match out_elem.sty { - ty::TyInt(i) => (Style::Int(true), i.bit_width().unwrap()), - ty::TyUint(u) => (Style::Int(false), u.bit_width().unwrap()), - ty::TyFloat(f) => (Style::Float, f.bit_width()), - _ => (Style::Unsupported, 0) - }; - - match (in_style, out_style) { - (Style::Int(in_is_signed), Style::Int(_)) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.trunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => if in_is_signed { - bx.sext(args[0].immediate(), llret_ty) - } else { - bx.zext(args[0].immediate(), llret_ty) - } - }) - } - (Style::Int(in_is_signed), Style::Float) => { - return Ok(if in_is_signed { - bx.sitofp(args[0].immediate(), llret_ty) - } else { - bx.uitofp(args[0].immediate(), llret_ty) - }) - } - (Style::Float, Style::Int(out_is_signed)) => { - return Ok(if out_is_signed { - bx.fptosi(args[0].immediate(), llret_ty) - } else { - bx.fptoui(args[0].immediate(), llret_ty) - }) - } - (Style::Float, Style::Float) => { - return Ok(match in_width.cmp(&out_width) { - Ordering::Greater => bx.fptrunc(args[0].immediate(), llret_ty), - Ordering::Equal => args[0].immediate(), - Ordering::Less => bx.fpext(args[0].immediate(), llret_ty) - }) - } - _ => {/* Unsupported. Fallthrough. */} - } - require!(false, - "unsupported cast from `{}` with element `{}` to `{}` with element `{}`", - in_ty, in_elem, - ret_ty, out_elem); - } - macro_rules! arith { - ($($name: ident: $($($p: ident),* => $call: ident),*;)*) => { - $(if name == stringify!($name) { - match in_elem.sty { - $($(ty::$p(_))|* => { - return Ok(bx.$call(args[0].immediate(), args[1].immediate())) - })* - _ => {}, - } - require!(false, - "unsupported operation on `{}` with element `{}`", - in_ty, - in_elem) - })* - } - } - arith! { - simd_add: TyUint, TyInt => add, TyFloat => fadd; - simd_sub: TyUint, TyInt => sub, TyFloat => fsub; - simd_mul: TyUint, TyInt => mul, TyFloat => fmul; - simd_div: TyUint => udiv, TyInt => sdiv, TyFloat => fdiv; - simd_rem: TyUint => urem, TyInt => srem, TyFloat => frem; - simd_shl: TyUint, TyInt => shl; - simd_shr: TyUint => lshr, TyInt => ashr; - simd_and: TyUint, TyInt => and; - simd_or: TyUint, TyInt => or; - simd_xor: TyUint, TyInt => xor; - simd_fmax: TyFloat => maxnum; - simd_fmin: TyFloat => minnum; - } - span_bug!(span, "unknown SIMD intrinsic"); -} - -// Returns the width of an int Ty, and if it's signed or not -// Returns None if the type is not an integer -// FIXME: there’s multiple of this functions, investigate using some of the already existing -// stuffs. -fn int_type_width_signed(ty: Ty, cx: &CodegenCx) -> Option<(u64, bool)> { - match ty.sty { - ty::TyInt(t) => Some((match t { - ast::IntTy::Isize => { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - tws => bug!("Unsupported target word size for isize: {}", tws), - } - }, - ast::IntTy::I8 => 8, - ast::IntTy::I16 => 16, - ast::IntTy::I32 => 32, - ast::IntTy::I64 => 64, - ast::IntTy::I128 => 128, - }, true)), - ty::TyUint(t) => Some((match t { - ast::UintTy::Usize => { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => 16, - "32" => 32, - "64" => 64, - tws => bug!("Unsupported target word size for usize: {}", tws), - } - }, - ast::UintTy::U8 => 8, - ast::UintTy::U16 => 16, - ast::UintTy::U32 => 32, - ast::UintTy::U64 => 64, - ast::UintTy::U128 => 128, - }, false)), - _ => None, - } -} - -// Returns the width of a float TypeVariant -// Returns None if the type is not a float -fn float_type_width<'tcx>(sty: &ty::TypeVariants<'tcx>) - -> Option { - use rustc::ty::TyFloat; - match *sty { - TyFloat(t) => Some(match t { - ast::FloatTy::F32 => 32, - ast::FloatTy::F64 => 64, - }), - _ => None, - } -} diff --git a/src/librustc_trans/lib.rs b/src/librustc_trans/lib.rs deleted file mode 100644 index 6db95657ce0..00000000000 --- a/src/librustc_trans/lib.rs +++ /dev/null @@ -1,390 +0,0 @@ -// Copyright 2012-2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Rust compiler. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] - -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(custom_attribute)] -#![feature(fs_read_write)] -#![allow(unused_attributes)] -#![feature(libc)] -#![feature(quote)] -#![feature(range_contains)] -#![feature(rustc_diagnostic_macros)] -#![feature(slice_sort_by_cached_key)] -#![feature(optin_builtin_traits)] -#![feature(inclusive_range_methods)] - -use rustc::dep_graph::WorkProduct; -use syntax_pos::symbol::Symbol; - -#[macro_use] extern crate bitflags; -extern crate flate2; -extern crate libc; -#[macro_use] extern crate rustc; -extern crate jobserver; -extern crate num_cpus; -extern crate rustc_mir; -extern crate rustc_allocator; -extern crate rustc_apfloat; -extern crate rustc_target; -#[macro_use] extern crate rustc_data_structures; -extern crate rustc_demangle; -extern crate rustc_incremental; -extern crate rustc_llvm as llvm; -extern crate rustc_platform_intrinsics as intrinsics; -extern crate rustc_trans_utils; - -#[macro_use] extern crate log; -#[macro_use] extern crate syntax; -extern crate syntax_pos; -extern crate rustc_errors as errors; -extern crate serialize; -extern crate cc; // Used to locate MSVC -extern crate tempdir; - -use back::bytecode::RLIB_BYTECODE_EXTENSION; - -pub use llvm_util::target_features; - -use std::any::Any; -use std::path::PathBuf; -use std::sync::mpsc; -use std::collections::BTreeMap; -use rustc_data_structures::sync::Lrc; - -use rustc::dep_graph::DepGraph; -use rustc::hir::def_id::CrateNum; -use rustc::middle::cstore::MetadataLoader; -use rustc::middle::cstore::{NativeLibrary, CrateSource, LibSource}; -use rustc::middle::lang_items::LangItem; -use rustc::session::{Session, CompileIncomplete}; -use rustc::session::config::{OutputFilenames, OutputType, PrintRequest}; -use rustc::ty::{self, TyCtxt}; -use rustc::util::nodemap::{FxHashSet, FxHashMap}; -use rustc_mir::monomorphize; -use rustc_trans_utils::trans_crate::TransCrate; - -mod diagnostics; - -mod back { - pub use rustc_trans_utils::symbol_names; - mod archive; - pub mod bytecode; - mod command; - pub mod linker; - pub mod link; - mod lto; - pub mod symbol_export; - pub mod write; - mod rpath; - mod wasm; -} - -mod abi; -mod allocator; -mod asm; -mod attributes; -mod base; -mod builder; -mod callee; -mod common; -mod consts; -mod context; -mod debuginfo; -mod declare; -mod glue; -mod intrinsic; -mod llvm_util; -mod metadata; -mod meth; -mod mir; -mod time_graph; -mod trans_item; -mod type_; -mod type_of; -mod value; - -pub struct LlvmTransCrate(()); - -impl !Send for LlvmTransCrate {} // Llvm is on a per-thread basis -impl !Sync for LlvmTransCrate {} - -impl LlvmTransCrate { - pub fn new() -> Box { - box LlvmTransCrate(()) - } -} - -impl TransCrate for LlvmTransCrate { - fn init(&self, sess: &Session) { - llvm_util::init(sess); // Make sure llvm is inited - } - - fn print(&self, req: PrintRequest, sess: &Session) { - match req { - PrintRequest::RelocationModels => { - println!("Available relocation models:"); - for &(name, _) in back::write::RELOC_MODEL_ARGS.iter() { - println!(" {}", name); - } - println!(""); - } - PrintRequest::CodeModels => { - println!("Available code models:"); - for &(name, _) in back::write::CODE_GEN_MODEL_ARGS.iter(){ - println!(" {}", name); - } - println!(""); - } - PrintRequest::TlsModels => { - println!("Available TLS models:"); - for &(name, _) in back::write::TLS_MODEL_ARGS.iter(){ - println!(" {}", name); - } - println!(""); - } - req => llvm_util::print(req, sess), - } - } - - fn print_passes(&self) { - llvm_util::print_passes(); - } - - fn print_version(&self) { - llvm_util::print_version(); - } - - fn diagnostics(&self) -> &[(&'static str, &'static str)] { - &DIAGNOSTICS - } - - fn target_features(&self, sess: &Session) -> Vec { - target_features(sess) - } - - fn metadata_loader(&self) -> Box { - box metadata::LlvmMetadataLoader - } - - fn provide(&self, providers: &mut ty::maps::Providers) { - back::symbol_names::provide(providers); - back::symbol_export::provide(providers); - base::provide(providers); - attributes::provide(providers); - } - - fn provide_extern(&self, providers: &mut ty::maps::Providers) { - back::symbol_export::provide_extern(providers); - base::provide_extern(providers); - attributes::provide_extern(providers); - } - - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver> - ) -> Box { - box base::trans_crate(tcx, rx) - } - - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete>{ - use rustc::util::common::time; - let (trans, work_products) = trans.downcast::<::back::write::OngoingCrateTranslation>() - .expect("Expected LlvmTransCrate's OngoingCrateTranslation, found Box") - .join(sess); - if sess.opts.debugging_opts.incremental_info { - back::write::dump_incremental_data(&trans); - } - - time(sess, - "serialize work products", - move || rustc_incremental::save_work_product_index(sess, &dep_graph, work_products)); - - sess.compile_status()?; - - if !sess.opts.output_types.keys().any(|&i| i == OutputType::Exe || - i == OutputType::Metadata) { - return Ok(()); - } - - // Run the linker on any artifacts that resulted from the LLVM run. - // This should produce either a finished executable or library. - time(sess, "linking", || { - back::link::link_binary(sess, &trans, outputs, &trans.crate_name.as_str()); - }); - - // Now that we won't touch anything in the incremental compilation directory - // any more, we can finalize it (which involves renaming it) - rustc_incremental::finalize_session_directory(sess, trans.link.crate_hash); - - Ok(()) - } -} - -/// This is the entrypoint for a hot plugged rustc_trans -#[no_mangle] -pub fn __rustc_codegen_backend() -> Box { - LlvmTransCrate::new() -} - -struct ModuleTranslation { - /// The name of the module. When the crate may be saved between - /// compilations, incremental compilation requires that name be - /// unique amongst **all** crates. Therefore, it should contain - /// something unique to this crate (e.g., a module path) as well - /// as the crate name and disambiguator. - name: String, - llmod_id: String, - source: ModuleSource, - kind: ModuleKind, -} - -#[derive(Copy, Clone, Debug, PartialEq)] -enum ModuleKind { - Regular, - Metadata, - Allocator, -} - -impl ModuleTranslation { - fn llvm(&self) -> Option<&ModuleLlvm> { - match self.source { - ModuleSource::Translated(ref llvm) => Some(llvm), - ModuleSource::Preexisting(_) => None, - } - } - - fn into_compiled_module(self, - emit_obj: bool, - emit_bc: bool, - emit_bc_compressed: bool, - outputs: &OutputFilenames) -> CompiledModule { - let pre_existing = match self.source { - ModuleSource::Preexisting(_) => true, - ModuleSource::Translated(_) => false, - }; - let object = if emit_obj { - Some(outputs.temp_path(OutputType::Object, Some(&self.name))) - } else { - None - }; - let bytecode = if emit_bc { - Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name))) - } else { - None - }; - let bytecode_compressed = if emit_bc_compressed { - Some(outputs.temp_path(OutputType::Bitcode, Some(&self.name)) - .with_extension(RLIB_BYTECODE_EXTENSION)) - } else { - None - }; - - CompiledModule { - llmod_id: self.llmod_id, - name: self.name.clone(), - kind: self.kind, - pre_existing, - object, - bytecode, - bytecode_compressed, - } - } -} - -#[derive(Debug)] -struct CompiledModule { - name: String, - llmod_id: String, - kind: ModuleKind, - pre_existing: bool, - object: Option, - bytecode: Option, - bytecode_compressed: Option, -} - -enum ModuleSource { - /// Copy the `.o` files or whatever from the incr. comp. directory. - Preexisting(WorkProduct), - - /// Rebuild from this LLVM module. - Translated(ModuleLlvm), -} - -#[derive(Debug)] -struct ModuleLlvm { - llcx: llvm::ContextRef, - llmod: llvm::ModuleRef, - tm: llvm::TargetMachineRef, -} - -unsafe impl Send for ModuleLlvm { } -unsafe impl Sync for ModuleLlvm { } - -impl Drop for ModuleLlvm { - fn drop(&mut self) { - unsafe { - llvm::LLVMDisposeModule(self.llmod); - llvm::LLVMContextDispose(self.llcx); - llvm::LLVMRustDisposeTargetMachine(self.tm); - } - } -} - -struct CrateTranslation { - crate_name: Symbol, - modules: Vec, - allocator_module: Option, - metadata_module: CompiledModule, - link: rustc::middle::cstore::LinkMeta, - metadata: rustc::middle::cstore::EncodedMetadata, - windows_subsystem: Option, - linker_info: back::linker::LinkerInfo, - crate_info: CrateInfo, -} - -// Misc info we load from metadata to persist beyond the tcx -struct CrateInfo { - panic_runtime: Option, - compiler_builtins: Option, - profiler_runtime: Option, - sanitizer_runtime: Option, - is_no_builtins: FxHashSet, - native_libraries: FxHashMap>>, - crate_name: FxHashMap, - used_libraries: Lrc>, - link_args: Lrc>, - used_crate_source: FxHashMap>, - used_crates_static: Vec<(CrateNum, LibSource)>, - used_crates_dynamic: Vec<(CrateNum, LibSource)>, - wasm_custom_sections: BTreeMap>, - wasm_imports: FxHashMap, - lang_item_to_crate: FxHashMap, - missing_lang_items: FxHashMap>, -} - -__build_diagnostic_array! { librustc_trans, DIAGNOSTICS } diff --git a/src/librustc_trans/llvm_util.rs b/src/librustc_trans/llvm_util.rs deleted file mode 100644 index bbd1c39a19e..00000000000 --- a/src/librustc_trans/llvm_util.rs +++ /dev/null @@ -1,255 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use syntax_pos::symbol::Symbol; -use back::write::create_target_machine; -use llvm; -use rustc::session::Session; -use rustc::session::config::PrintRequest; -use libc::c_int; -use std::ffi::CString; -use syntax::feature_gate::UnstableFeatures; - -use std::sync::atomic::{AtomicBool, Ordering}; -use std::sync::Once; - -static POISONED: AtomicBool = AtomicBool::new(false); -static INIT: Once = Once::new(); - -pub(crate) fn init(sess: &Session) { - unsafe { - // Before we touch LLVM, make sure that multithreading is enabled. - INIT.call_once(|| { - if llvm::LLVMStartMultithreaded() != 1 { - // use an extra bool to make sure that all future usage of LLVM - // cannot proceed despite the Once not running more than once. - POISONED.store(true, Ordering::SeqCst); - } - - configure_llvm(sess); - }); - - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); - } - } -} - -fn require_inited() { - INIT.call_once(|| bug!("llvm is not initialized")); - if POISONED.load(Ordering::SeqCst) { - bug!("couldn't enable multi-threaded LLVM"); - } -} - -unsafe fn configure_llvm(sess: &Session) { - let mut llvm_c_strs = Vec::new(); - let mut llvm_args = Vec::new(); - - { - let mut add = |arg: &str| { - let s = CString::new(arg).unwrap(); - llvm_args.push(s.as_ptr()); - llvm_c_strs.push(s); - }; - add("rustc"); // fake program name - if sess.time_llvm_passes() { add("-time-passes"); } - if sess.print_llvm_passes() { add("-debug-pass=Structure"); } - if sess.opts.debugging_opts.disable_instrumentation_preinliner { - add("-disable-preinline"); - } - - for arg in &sess.opts.cg.llvm_args { - add(&(*arg)); - } - } - - llvm::LLVMInitializePasses(); - - llvm::initialize_available_targets(); - - llvm::LLVMRustSetLLVMOptions(llvm_args.len() as c_int, - llvm_args.as_ptr()); -} - -// WARNING: the features after applying `to_llvm_feature` must be known -// to LLVM or the feature detection code will walk past the end of the feature -// array, leading to crashes. - -const ARM_WHITELIST: &[(&str, Option<&str>)] = &[ - ("neon", Some("arm_target_feature")), - ("v7", Some("arm_target_feature")), - ("vfp2", Some("arm_target_feature")), - ("vfp3", Some("arm_target_feature")), - ("vfp4", Some("arm_target_feature")), -]; - -const AARCH64_WHITELIST: &[(&str, Option<&str>)] = &[ - ("fp", Some("aarch64_target_feature")), - ("neon", Some("aarch64_target_feature")), - ("sve", Some("aarch64_target_feature")), - ("crc", Some("aarch64_target_feature")), - ("crypto", Some("aarch64_target_feature")), - ("ras", Some("aarch64_target_feature")), - ("lse", Some("aarch64_target_feature")), - ("rdm", Some("aarch64_target_feature")), - ("fp16", Some("aarch64_target_feature")), - ("rcpc", Some("aarch64_target_feature")), - ("dotprod", Some("aarch64_target_feature")), - ("v8.1a", Some("aarch64_target_feature")), - ("v8.2a", Some("aarch64_target_feature")), - ("v8.3a", Some("aarch64_target_feature")), -]; - -const X86_WHITELIST: &[(&str, Option<&str>)] = &[ - ("aes", None), - ("avx", None), - ("avx2", None), - ("avx512bw", Some("avx512_target_feature")), - ("avx512cd", Some("avx512_target_feature")), - ("avx512dq", Some("avx512_target_feature")), - ("avx512er", Some("avx512_target_feature")), - ("avx512f", Some("avx512_target_feature")), - ("avx512ifma", Some("avx512_target_feature")), - ("avx512pf", Some("avx512_target_feature")), - ("avx512vbmi", Some("avx512_target_feature")), - ("avx512vl", Some("avx512_target_feature")), - ("avx512vpopcntdq", Some("avx512_target_feature")), - ("bmi1", None), - ("bmi2", None), - ("fma", None), - ("fxsr", None), - ("lzcnt", None), - ("mmx", Some("mmx_target_feature")), - ("pclmulqdq", None), - ("popcnt", None), - ("rdrand", None), - ("rdseed", None), - ("sha", None), - ("sse", None), - ("sse2", None), - ("sse3", None), - ("sse4.1", None), - ("sse4.2", None), - ("sse4a", Some("sse4a_target_feature")), - ("ssse3", None), - ("tbm", Some("tbm_target_feature")), - ("xsave", None), - ("xsavec", None), - ("xsaveopt", None), - ("xsaves", None), -]; - -const HEXAGON_WHITELIST: &[(&str, Option<&str>)] = &[ - ("hvx", Some("hexagon_target_feature")), - ("hvx-double", Some("hexagon_target_feature")), -]; - -const POWERPC_WHITELIST: &[(&str, Option<&str>)] = &[ - ("altivec", Some("powerpc_target_feature")), - ("power8-altivec", Some("powerpc_target_feature")), - ("power9-altivec", Some("powerpc_target_feature")), - ("power8-vector", Some("powerpc_target_feature")), - ("power9-vector", Some("powerpc_target_feature")), - ("vsx", Some("powerpc_target_feature")), -]; - -const MIPS_WHITELIST: &[(&str, Option<&str>)] = &[ - ("fp64", Some("mips_target_feature")), - ("msa", Some("mips_target_feature")), -]; - -/// When rustdoc is running, provide a list of all known features so that all their respective -/// primtives may be documented. -/// -/// IMPORTANT: If you're adding another whitelist to the above lists, make sure to add it to this -/// iterator! -pub fn all_known_features() -> impl Iterator)> { - ARM_WHITELIST.iter().cloned() - .chain(AARCH64_WHITELIST.iter().cloned()) - .chain(X86_WHITELIST.iter().cloned()) - .chain(HEXAGON_WHITELIST.iter().cloned()) - .chain(POWERPC_WHITELIST.iter().cloned()) - .chain(MIPS_WHITELIST.iter().cloned()) -} - -pub fn to_llvm_feature<'a>(sess: &Session, s: &'a str) -> &'a str { - let arch = if sess.target.target.arch == "x86_64" { - "x86" - } else { - &*sess.target.target.arch - }; - match (arch, s) { - ("x86", "pclmulqdq") => "pclmul", - ("x86", "rdrand") => "rdrnd", - ("x86", "bmi1") => "bmi", - ("aarch64", "fp") => "fp-armv8", - ("aarch64", "fp16") => "fullfp16", - (_, s) => s, - } -} - -pub fn target_features(sess: &Session) -> Vec { - let target_machine = create_target_machine(sess, true); - target_feature_whitelist(sess) - .iter() - .filter_map(|&(feature, gate)| { - if UnstableFeatures::from_environment().is_nightly_build() || gate.is_none() { - Some(feature) - } else { - None - } - }) - .filter(|feature| { - let llvm_feature = to_llvm_feature(sess, feature); - let cstr = CString::new(llvm_feature).unwrap(); - unsafe { llvm::LLVMRustHasFeature(target_machine, cstr.as_ptr()) } - }) - .map(|feature| Symbol::intern(feature)).collect() -} - -pub fn target_feature_whitelist(sess: &Session) - -> &'static [(&'static str, Option<&'static str>)] -{ - match &*sess.target.target.arch { - "arm" => ARM_WHITELIST, - "aarch64" => AARCH64_WHITELIST, - "x86" | "x86_64" => X86_WHITELIST, - "hexagon" => HEXAGON_WHITELIST, - "mips" | "mips64" => MIPS_WHITELIST, - "powerpc" | "powerpc64" => POWERPC_WHITELIST, - _ => &[], - } -} - -pub fn print_version() { - // Can be called without initializing LLVM - unsafe { - println!("LLVM version: {}.{}", - llvm::LLVMRustVersionMajor(), llvm::LLVMRustVersionMinor()); - } -} - -pub fn print_passes() { - // Can be called without initializing LLVM - unsafe { llvm::LLVMRustPrintPasses(); } -} - -pub(crate) fn print(req: PrintRequest, sess: &Session) { - require_inited(); - let tm = create_target_machine(sess, true); - unsafe { - match req { - PrintRequest::TargetCPUs => llvm::LLVMRustPrintTargetCPUs(tm), - PrintRequest::TargetFeatures => llvm::LLVMRustPrintTargetFeatures(tm), - _ => bug!("rustc_trans can't handle print request: {:?}", req), - } - } -} diff --git a/src/librustc_trans/metadata.rs b/src/librustc_trans/metadata.rs deleted file mode 100644 index 144baa65c1b..00000000000 --- a/src/librustc_trans/metadata.rs +++ /dev/null @@ -1,124 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::util::common; -use rustc::middle::cstore::MetadataLoader; -use rustc_target::spec::Target; -use llvm; -use llvm::{False, ObjectFile, mk_section_iter}; -use llvm::archive_ro::ArchiveRO; - -use rustc_data_structures::owning_ref::OwningRef; -use std::path::Path; -use std::ptr; -use std::slice; - -pub use rustc_data_structures::sync::MetadataRef; - -pub const METADATA_FILENAME: &str = "rust.metadata.bin"; - -pub struct LlvmMetadataLoader; - -impl MetadataLoader for LlvmMetadataLoader { - fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - // Use ArchiveRO for speed here, it's backed by LLVM and uses mmap - // internally to read the file. We also avoid even using a memcpy by - // just keeping the archive along while the metadata is in use. - let archive = ArchiveRO::open(filename) - .map(|ar| OwningRef::new(box ar)) - .map_err(|e| { - debug!("llvm didn't like `{}`: {}", filename.display(), e); - format!("failed to read rlib metadata in '{}': {}", filename.display(), e) - })?; - let buf: OwningRef<_, [u8]> = archive - .try_map(|ar| { - ar.iter() - .filter_map(|s| s.ok()) - .find(|sect| sect.name() == Some(METADATA_FILENAME)) - .map(|s| s.data()) - .ok_or_else(|| { - debug!("didn't find '{}' in the archive", METADATA_FILENAME); - format!("failed to read rlib metadata: '{}'", - filename.display()) - }) - })?; - Ok(rustc_erase_owner!(buf)) - } - - fn get_dylib_metadata(&self, - target: &Target, - filename: &Path) - -> Result { - unsafe { - let buf = common::path2cstr(filename); - let mb = llvm::LLVMRustCreateMemoryBufferWithContentsOfFile(buf.as_ptr()); - if mb as isize == 0 { - return Err(format!("error reading library: '{}'", filename.display())); - } - let of = ObjectFile::new(mb) - .map(|of| OwningRef::new(box of)) - .ok_or_else(|| format!("provided path not an object file: '{}'", - filename.display()))?; - let buf = of.try_map(|of| search_meta_section(of, target, filename))?; - Ok(rustc_erase_owner!(buf)) - } - } -} - -fn search_meta_section<'a>(of: &'a ObjectFile, - target: &Target, - filename: &Path) - -> Result<&'a [u8], String> { - unsafe { - let si = mk_section_iter(of.llof); - while llvm::LLVMIsSectionIteratorAtEnd(of.llof, si.llsi) == False { - let mut name_buf = ptr::null(); - let name_len = llvm::LLVMRustGetSectionName(si.llsi, &mut name_buf); - let name = slice::from_raw_parts(name_buf as *const u8, name_len as usize).to_vec(); - let name = String::from_utf8(name).unwrap(); - debug!("get_metadata_section: name {}", name); - if read_metadata_section_name(target) == name { - let cbuf = llvm::LLVMGetSectionContents(si.llsi); - let csz = llvm::LLVMGetSectionSize(si.llsi) as usize; - // The buffer is valid while the object file is around - let buf: &'a [u8] = slice::from_raw_parts(cbuf as *const u8, csz); - return Ok(buf); - } - llvm::LLVMMoveToNextSection(si.llsi); - } - } - Err(format!("metadata not found: '{}'", filename.display())) -} - -pub fn metadata_section_name(target: &Target) -> &'static str { - // Historical note: - // - // When using link.exe it was seen that the section name `.note.rustc` - // was getting shortened to `.note.ru`, and according to the PE and COFF - // specification: - // - // > Executable images do not use a string table and do not support - // > section names longer than 8 characters - // - // https://msdn.microsoft.com/en-us/library/windows/hardware/gg463119.aspx - // - // As a result, we choose a slightly shorter name! As to why - // `.note.rustc` works on MinGW, that's another good question... - - if target.options.is_like_osx { - "__DATA,.rustc" - } else { - ".rustc" - } -} - -fn read_metadata_section_name(_target: &Target) -> &'static str { - ".rustc" -} diff --git a/src/librustc_trans/meth.rs b/src/librustc_trans/meth.rs deleted file mode 100644 index 21bbdf31dcb..00000000000 --- a/src/librustc_trans/meth.rs +++ /dev/null @@ -1,115 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::ValueRef; -use abi::{FnType, FnTypeExt}; -use callee; -use common::*; -use builder::Builder; -use consts; -use monomorphize; -use type_::Type; -use value::Value; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::HasDataLayout; -use debuginfo; - -#[derive(Copy, Clone, Debug)] -pub struct VirtualIndex(u64); - -pub const DESTRUCTOR: VirtualIndex = VirtualIndex(0); -pub const SIZE: VirtualIndex = VirtualIndex(1); -pub const ALIGN: VirtualIndex = VirtualIndex(2); - -impl<'a, 'tcx> VirtualIndex { - pub fn from_index(index: usize) -> Self { - VirtualIndex(index as u64 + 3) - } - - pub fn get_fn(self, bx: &Builder<'a, 'tcx>, - llvtable: ValueRef, - fn_ty: &FnType<'tcx, Ty<'tcx>>) -> ValueRef { - // Load the data pointer from the object. - debug!("get_fn({:?}, {:?})", Value(llvtable), self); - - let llvtable = bx.pointercast(llvtable, fn_ty.llvm_type(bx.cx).ptr_to().ptr_to()); - let ptr_align = bx.tcx().data_layout.pointer_align; - let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), ptr_align); - bx.nonnull_metadata(ptr); - // Vtable loads are invariant - bx.set_invariant_load(ptr); - ptr - } - - pub fn get_usize(self, bx: &Builder<'a, 'tcx>, llvtable: ValueRef) -> ValueRef { - // Load the data pointer from the object. - debug!("get_int({:?}, {:?})", Value(llvtable), self); - - let llvtable = bx.pointercast(llvtable, Type::isize(bx.cx).ptr_to()); - let usize_align = bx.tcx().data_layout.pointer_align; - let ptr = bx.load(bx.inbounds_gep(llvtable, &[C_usize(bx.cx, self.0)]), usize_align); - // Vtable loads are invariant - bx.set_invariant_load(ptr); - ptr - } -} - -/// Creates a dynamic vtable for the given type and vtable origin. -/// This is used only for objects. -/// -/// The vtables are cached instead of created on every call. -/// -/// The `trait_ref` encodes the erased self type. Hence if we are -/// making an object `Foo` from a value of type `Foo`, then -/// `trait_ref` would map `T:Trait`. -pub fn get_vtable<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - ty: Ty<'tcx>, - trait_ref: Option>) - -> ValueRef -{ - let tcx = cx.tcx; - - debug!("get_vtable(ty={:?}, trait_ref={:?})", ty, trait_ref); - - // Check the cache. - if let Some(&val) = cx.vtables.borrow().get(&(ty, trait_ref)) { - return val; - } - - // Not in the cache. Build it. - let nullptr = C_null(Type::i8p(cx)); - - let (size, align) = cx.size_and_align_of(ty); - let mut components: Vec<_> = [ - callee::get_fn(cx, monomorphize::resolve_drop_in_place(cx.tcx, ty)), - C_usize(cx, size.bytes()), - C_usize(cx, align.abi()) - ].iter().cloned().collect(); - - if let Some(trait_ref) = trait_ref { - let trait_ref = trait_ref.with_self_ty(tcx, ty); - let methods = tcx.vtable_methods(trait_ref); - let methods = methods.iter().cloned().map(|opt_mth| { - opt_mth.map_or(nullptr, |(def_id, substs)| { - callee::resolve_and_get_fn(cx, def_id, substs) - }) - }); - components.extend(methods); - } - - let vtable_const = C_struct(cx, &components, false); - let align = cx.data_layout().pointer_align; - let vtable = consts::addr_of(cx, vtable_const, align, "vtable"); - - debuginfo::create_vtable_metadata(cx, ty, vtable); - - cx.vtables.borrow_mut().insert((ty, trait_ref), vtable); - vtable -} diff --git a/src/librustc_trans/mir/analyze.rs b/src/librustc_trans/mir/analyze.rs deleted file mode 100644 index 9e5298eb736..00000000000 --- a/src/librustc_trans/mir/analyze.rs +++ /dev/null @@ -1,361 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! An analysis to determine which locals require allocas and -//! which do not. - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::control_flow_graph::dominators::Dominators; -use rustc_data_structures::indexed_vec::{Idx, IndexVec}; -use rustc::mir::{self, Location, TerminatorKind}; -use rustc::mir::visit::{Visitor, PlaceContext}; -use rustc::mir::traversal; -use rustc::ty; -use rustc::ty::layout::LayoutOf; -use type_of::LayoutLlvmExt; -use super::FunctionCx; - -pub fn non_ssa_locals<'a, 'tcx>(fx: &FunctionCx<'a, 'tcx>) -> BitVector { - let mir = fx.mir; - let mut analyzer = LocalAnalyzer::new(fx); - - analyzer.visit_mir(mir); - - for (index, ty) in mir.local_decls.iter().map(|l| l.ty).enumerate() { - let ty = fx.monomorphize(&ty); - debug!("local {} has type {:?}", index, ty); - let layout = fx.cx.layout_of(ty); - if layout.is_llvm_immediate() { - // These sorts of types are immediates that we can store - // in an ValueRef without an alloca. - } else if layout.is_llvm_scalar_pair() { - // We allow pairs and uses of any of their 2 fields. - } else { - // These sorts of types require an alloca. Note that - // is_llvm_immediate() may *still* be true, particularly - // for newtypes, but we currently force some types - // (e.g. structs) into an alloca unconditionally, just so - // that we don't have to deal with having two pathways - // (gep vs extractvalue etc). - analyzer.not_ssa(mir::Local::new(index)); - } - } - - analyzer.non_ssa_locals -} - -struct LocalAnalyzer<'mir, 'a: 'mir, 'tcx: 'a> { - fx: &'mir FunctionCx<'a, 'tcx>, - dominators: Dominators, - non_ssa_locals: BitVector, - // The location of the first visited direct assignment to each - // local, or an invalid location (out of bounds `block` index). - first_assignment: IndexVec -} - -impl<'mir, 'a, 'tcx> LocalAnalyzer<'mir, 'a, 'tcx> { - fn new(fx: &'mir FunctionCx<'a, 'tcx>) -> LocalAnalyzer<'mir, 'a, 'tcx> { - let invalid_location = - mir::BasicBlock::new(fx.mir.basic_blocks().len()).start_location(); - let mut analyzer = LocalAnalyzer { - fx, - dominators: fx.mir.dominators(), - non_ssa_locals: BitVector::new(fx.mir.local_decls.len()), - first_assignment: IndexVec::from_elem(invalid_location, &fx.mir.local_decls) - }; - - // Arguments get assigned to by means of the function being called - for arg in fx.mir.args_iter() { - analyzer.first_assignment[arg] = mir::START_BLOCK.start_location(); - } - - analyzer - } - - fn first_assignment(&self, local: mir::Local) -> Option { - let location = self.first_assignment[local]; - if location.block.index() < self.fx.mir.basic_blocks().len() { - Some(location) - } else { - None - } - } - - fn not_ssa(&mut self, local: mir::Local) { - debug!("marking {:?} as non-SSA", local); - self.non_ssa_locals.insert(local.index()); - } - - fn assign(&mut self, local: mir::Local, location: Location) { - if self.first_assignment(local).is_some() { - self.not_ssa(local); - } else { - self.first_assignment[local] = location; - } - } -} - -impl<'mir, 'a, 'tcx> Visitor<'tcx> for LocalAnalyzer<'mir, 'a, 'tcx> { - fn visit_assign(&mut self, - block: mir::BasicBlock, - place: &mir::Place<'tcx>, - rvalue: &mir::Rvalue<'tcx>, - location: Location) { - debug!("visit_assign(block={:?}, place={:?}, rvalue={:?})", block, place, rvalue); - - if let mir::Place::Local(index) = *place { - self.assign(index, location); - if !self.fx.rvalue_creates_operand(rvalue) { - self.not_ssa(index); - } - } else { - self.visit_place(place, PlaceContext::Store, location); - } - - self.visit_rvalue(rvalue, location); - } - - fn visit_terminator_kind(&mut self, - block: mir::BasicBlock, - kind: &mir::TerminatorKind<'tcx>, - location: Location) { - let check = match *kind { - mir::TerminatorKind::Call { - func: mir::Operand::Constant(ref c), - ref args, .. - } => match c.ty.sty { - ty::TyFnDef(did, _) => Some((did, args)), - _ => None, - }, - _ => None, - }; - if let Some((def_id, args)) = check { - if Some(def_id) == self.fx.cx.tcx.lang_items().box_free_fn() { - // box_free(x) shares with `drop x` the property that it - // is not guaranteed to be statically dominated by the - // definition of x, so x must always be in an alloca. - if let mir::Operand::Move(ref place) = args[0] { - self.visit_place(place, PlaceContext::Drop, location); - } - } - } - - self.super_terminator_kind(block, kind, location); - } - - fn visit_place(&mut self, - place: &mir::Place<'tcx>, - context: PlaceContext<'tcx>, - location: Location) { - debug!("visit_place(place={:?}, context={:?})", place, context); - let cx = self.fx.cx; - - if let mir::Place::Projection(ref proj) = *place { - // Allow uses of projections that are ZSTs or from scalar fields. - let is_consume = match context { - PlaceContext::Copy | PlaceContext::Move => true, - _ => false - }; - if is_consume { - let base_ty = proj.base.ty(self.fx.mir, cx.tcx); - let base_ty = self.fx.monomorphize(&base_ty); - - // ZSTs don't require any actual memory access. - let elem_ty = base_ty.projection_ty(cx.tcx, &proj.elem).to_ty(cx.tcx); - let elem_ty = self.fx.monomorphize(&elem_ty); - if cx.layout_of(elem_ty).is_zst() { - return; - } - - if let mir::ProjectionElem::Field(..) = proj.elem { - let layout = cx.layout_of(base_ty.to_ty(cx.tcx)); - if layout.is_llvm_immediate() || layout.is_llvm_scalar_pair() { - // Recurse with the same context, instead of `Projection`, - // potentially stopping at non-operand projections, - // which would trigger `not_ssa` on locals. - self.visit_place(&proj.base, context, location); - return; - } - } - } - - // A deref projection only reads the pointer, never needs the place. - if let mir::ProjectionElem::Deref = proj.elem { - return self.visit_place(&proj.base, PlaceContext::Copy, location); - } - } - - self.super_place(place, context, location); - } - - fn visit_local(&mut self, - &local: &mir::Local, - context: PlaceContext<'tcx>, - location: Location) { - match context { - PlaceContext::Call => { - self.assign(local, location); - } - - PlaceContext::StorageLive | - PlaceContext::StorageDead | - PlaceContext::Validate => {} - - PlaceContext::Copy | - PlaceContext::Move => { - // Reads from uninitialized variables (e.g. in dead code, after - // optimizations) require locals to be in (uninitialized) memory. - // NB: there can be uninitialized reads of a local visited after - // an assignment to that local, if they happen on disjoint paths. - let ssa_read = match self.first_assignment(local) { - Some(assignment_location) => { - assignment_location.dominates(location, &self.dominators) - } - None => false - }; - if !ssa_read { - self.not_ssa(local); - } - } - - PlaceContext::Inspect | - PlaceContext::Store | - PlaceContext::AsmOutput | - PlaceContext::Borrow { .. } | - PlaceContext::Projection(..) => { - self.not_ssa(local); - } - - PlaceContext::Drop => { - let ty = mir::Place::Local(local).ty(self.fx.mir, self.fx.cx.tcx); - let ty = self.fx.monomorphize(&ty.to_ty(self.fx.cx.tcx)); - - // Only need the place if we're actually dropping it. - if self.fx.cx.type_needs_drop(ty) { - self.not_ssa(local); - } - } - } - } -} - -#[derive(Copy, Clone, Debug, PartialEq, Eq)] -pub enum CleanupKind { - NotCleanup, - Funclet, - Internal { funclet: mir::BasicBlock } -} - -impl CleanupKind { - pub fn funclet_bb(self, for_bb: mir::BasicBlock) -> Option { - match self { - CleanupKind::NotCleanup => None, - CleanupKind::Funclet => Some(for_bb), - CleanupKind::Internal { funclet } => Some(funclet), - } - } -} - -pub fn cleanup_kinds<'a, 'tcx>(mir: &mir::Mir<'tcx>) -> IndexVec { - fn discover_masters<'tcx>(result: &mut IndexVec, - mir: &mir::Mir<'tcx>) { - for (bb, data) in mir.basic_blocks().iter_enumerated() { - match data.terminator().kind { - TerminatorKind::Goto { .. } | - TerminatorKind::Resume | - TerminatorKind::Abort | - TerminatorKind::Return | - TerminatorKind::GeneratorDrop | - TerminatorKind::Unreachable | - TerminatorKind::SwitchInt { .. } | - TerminatorKind::Yield { .. } | - TerminatorKind::FalseEdges { .. } | - TerminatorKind::FalseUnwind { .. } => { - /* nothing to do */ - } - TerminatorKind::Call { cleanup: unwind, .. } | - TerminatorKind::Assert { cleanup: unwind, .. } | - TerminatorKind::DropAndReplace { unwind, .. } | - TerminatorKind::Drop { unwind, .. } => { - if let Some(unwind) = unwind { - debug!("cleanup_kinds: {:?}/{:?} registering {:?} as funclet", - bb, data, unwind); - result[unwind] = CleanupKind::Funclet; - } - } - } - } - } - - fn propagate<'tcx>(result: &mut IndexVec, - mir: &mir::Mir<'tcx>) { - let mut funclet_succs = IndexVec::from_elem(None, mir.basic_blocks()); - - let mut set_successor = |funclet: mir::BasicBlock, succ| { - match funclet_succs[funclet] { - ref mut s @ None => { - debug!("set_successor: updating successor of {:?} to {:?}", - funclet, succ); - *s = Some(succ); - }, - Some(s) => if s != succ { - span_bug!(mir.span, "funclet {:?} has 2 parents - {:?} and {:?}", - funclet, s, succ); - } - } - }; - - for (bb, data) in traversal::reverse_postorder(mir) { - let funclet = match result[bb] { - CleanupKind::NotCleanup => continue, - CleanupKind::Funclet => bb, - CleanupKind::Internal { funclet } => funclet, - }; - - debug!("cleanup_kinds: {:?}/{:?}/{:?} propagating funclet {:?}", - bb, data, result[bb], funclet); - - for &succ in data.terminator().successors() { - let kind = result[succ]; - debug!("cleanup_kinds: propagating {:?} to {:?}/{:?}", - funclet, succ, kind); - match kind { - CleanupKind::NotCleanup => { - result[succ] = CleanupKind::Internal { funclet: funclet }; - } - CleanupKind::Funclet => { - if funclet != succ { - set_successor(funclet, succ); - } - } - CleanupKind::Internal { funclet: succ_funclet } => { - if funclet != succ_funclet { - // `succ` has 2 different funclet going into it, so it must - // be a funclet by itself. - - debug!("promoting {:?} to a funclet and updating {:?}", succ, - succ_funclet); - result[succ] = CleanupKind::Funclet; - set_successor(succ_funclet, succ); - set_successor(funclet, succ); - } - } - } - } - } - } - - let mut result = IndexVec::from_elem(CleanupKind::NotCleanup, mir.basic_blocks()); - - discover_masters(&mut result, mir); - propagate(&mut result, mir); - debug!("cleanup_kinds: result={:?}", result); - result -} diff --git a/src/librustc_trans/mir/block.rs b/src/librustc_trans/mir/block.rs deleted file mode 100644 index e4989da36c0..00000000000 --- a/src/librustc_trans/mir/block.rs +++ /dev/null @@ -1,895 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef, BasicBlockRef}; -use rustc::middle::lang_items; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::mir; -use rustc::mir::interpret::EvalErrorKind; -use abi::{Abi, ArgType, ArgTypeExt, FnType, FnTypeExt, LlvmType, PassMode}; -use base; -use callee; -use builder::{Builder, MemFlags}; -use common::{self, C_bool, C_str_slice, C_struct, C_u32, C_uint_big, C_undef}; -use consts; -use meth; -use monomorphize; -use type_of::LayoutLlvmExt; -use type_::Type; - -use syntax::symbol::Symbol; -use syntax_pos::Pos; - -use super::{FunctionCx, LocalRef}; -use super::place::PlaceRef; -use super::operand::OperandRef; -use super::operand::OperandValue::{Pair, Ref, Immediate}; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_block(&mut self, bb: mir::BasicBlock) { - let mut bx = self.build_block(bb); - let data = &self.mir[bb]; - - debug!("trans_block({:?}={:?})", bb, data); - - for statement in &data.statements { - bx = self.trans_statement(bx, statement); - } - - self.trans_terminator(bx, bb, data.terminator()); - } - - fn trans_terminator(&mut self, - mut bx: Builder<'a, 'tcx>, - bb: mir::BasicBlock, - terminator: &mir::Terminator<'tcx>) - { - debug!("trans_terminator: {:?}", terminator); - - // Create the cleanup bundle, if needed. - let tcx = bx.tcx(); - let span = terminator.source_info.span; - let funclet_bb = self.cleanup_kinds[bb].funclet_bb(bb); - let funclet = funclet_bb.and_then(|funclet_bb| self.funclets[funclet_bb].as_ref()); - - let cleanup_pad = funclet.map(|lp| lp.cleanuppad()); - let cleanup_bundle = funclet.map(|l| l.bundle()); - - let lltarget = |this: &mut Self, target: mir::BasicBlock| { - let lltarget = this.blocks[target]; - let target_funclet = this.cleanup_kinds[target].funclet_bb(target); - match (funclet_bb, target_funclet) { - (None, None) => (lltarget, false), - (Some(f), Some(t_f)) - if f == t_f || !base::wants_msvc_seh(tcx.sess) - => (lltarget, false), - (None, Some(_)) => { - // jump *into* cleanup - need a landing pad if GNU - (this.landing_pad_to(target), false) - } - (Some(_), None) => span_bug!(span, "{:?} - jump out of cleanup?", terminator), - (Some(_), Some(_)) => { - (this.landing_pad_to(target), true) - } - } - }; - - let llblock = |this: &mut Self, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // MSVC cross-funclet jump - need a trampoline - - debug!("llblock: creating cleanup trampoline for {:?}", target); - let name = &format!("{:?}_cleanup_trampoline_{:?}", bb, target); - let trampoline = this.new_block(name); - trampoline.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); - trampoline.llbb() - } else { - lltarget - } - }; - - let funclet_br = |this: &mut Self, bx: Builder, target: mir::BasicBlock| { - let (lltarget, is_cleanupret) = lltarget(this, target); - if is_cleanupret { - // micro-optimization: generate a `ret` rather than a jump - // to a trampoline. - bx.cleanup_ret(cleanup_pad.unwrap(), Some(lltarget)); - } else { - bx.br(lltarget); - } - }; - - let do_call = | - this: &mut Self, - bx: Builder<'a, 'tcx>, - fn_ty: FnType<'tcx, Ty<'tcx>>, - fn_ptr: ValueRef, - llargs: &[ValueRef], - destination: Option<(ReturnDest<'tcx>, mir::BasicBlock)>, - cleanup: Option - | { - if let Some(cleanup) = cleanup { - let ret_bx = if let Some((_, target)) = destination { - this.blocks[target] - } else { - this.unreachable_block() - }; - let invokeret = bx.invoke(fn_ptr, - &llargs, - ret_bx, - llblock(this, cleanup), - cleanup_bundle); - fn_ty.apply_attrs_callsite(&bx, invokeret); - - if let Some((ret_dest, target)) = destination { - let ret_bx = this.build_block(target); - this.set_debug_loc(&ret_bx, terminator.source_info); - this.store_return(&ret_bx, ret_dest, &fn_ty.ret, invokeret); - } - } else { - let llret = bx.call(fn_ptr, &llargs, cleanup_bundle); - fn_ty.apply_attrs_callsite(&bx, llret); - if this.mir[bb].is_cleanup { - // Cleanup is always the cold path. Don't inline - // drop glue. Also, when there is a deeply-nested - // struct, there are "symmetry" issues that cause - // exponential inlining - see issue #41696. - llvm::Attribute::NoInline.apply_callsite(llvm::AttributePlace::Function, llret); - } - - if let Some((ret_dest, target)) = destination { - this.store_return(&bx, ret_dest, &fn_ty.ret, llret); - funclet_br(this, bx, target); - } else { - bx.unreachable(); - } - } - }; - - self.set_debug_loc(&bx, terminator.source_info); - match terminator.kind { - mir::TerminatorKind::Resume => { - if let Some(cleanup_pad) = cleanup_pad { - bx.cleanup_ret(cleanup_pad, None); - } else { - let slot = self.get_personality_slot(&bx); - let lp0 = slot.project_field(&bx, 0).load(&bx).immediate(); - let lp1 = slot.project_field(&bx, 1).load(&bx).immediate(); - slot.storage_dead(&bx); - - if !bx.sess().target.target.options.custom_unwind_resume { - let mut lp = C_undef(self.landing_pad_type()); - lp = bx.insert_value(lp, lp0, 0); - lp = bx.insert_value(lp, lp1, 1); - bx.resume(lp); - } else { - bx.call(bx.cx.eh_unwind_resume(), &[lp0], cleanup_bundle); - bx.unreachable(); - } - } - } - - mir::TerminatorKind::Abort => { - // Call core::intrinsics::abort() - let fnname = bx.cx.get_intrinsic(&("llvm.trap")); - bx.call(fnname, &[], None); - bx.unreachable(); - } - - mir::TerminatorKind::Goto { target } => { - funclet_br(self, bx, target); - } - - mir::TerminatorKind::SwitchInt { ref discr, switch_ty, ref values, ref targets } => { - let discr = self.trans_operand(&bx, discr); - if switch_ty == bx.tcx().types.bool { - let lltrue = llblock(self, targets[0]); - let llfalse = llblock(self, targets[1]); - if let [0] = values[..] { - bx.cond_br(discr.immediate(), llfalse, lltrue); - } else { - assert_eq!(&values[..], &[1]); - bx.cond_br(discr.immediate(), lltrue, llfalse); - } - } else { - let (otherwise, targets) = targets.split_last().unwrap(); - let switch = bx.switch(discr.immediate(), - llblock(self, *otherwise), values.len()); - let switch_llty = bx.cx.layout_of(switch_ty).immediate_llvm_type(bx.cx); - for (&value, target) in values.iter().zip(targets) { - let llval = C_uint_big(switch_llty, value); - let llbb = llblock(self, *target); - bx.add_case(switch, llval, llbb) - } - } - } - - mir::TerminatorKind::Return => { - let llval = match self.fn_ty.ret.mode { - PassMode::Ignore | PassMode::Indirect(_) => { - bx.ret_void(); - return; - } - - PassMode::Direct(_) | PassMode::Pair(..) => { - let op = self.trans_consume(&bx, &mir::Place::Local(mir::RETURN_PLACE)); - if let Ref(llval, align) = op.val { - bx.load(llval, align) - } else { - op.immediate_or_packed_pair(&bx) - } - } - - PassMode::Cast(cast_ty) => { - let op = match self.locals[mir::RETURN_PLACE] { - LocalRef::Operand(Some(op)) => op, - LocalRef::Operand(None) => bug!("use of return before def"), - LocalRef::Place(tr_place) => { - OperandRef { - val: Ref(tr_place.llval, tr_place.align), - layout: tr_place.layout - } - } - }; - let llslot = match op.val { - Immediate(_) | Pair(..) => { - let scratch = PlaceRef::alloca(&bx, self.fn_ty.ret.layout, "ret"); - op.val.store(&bx, scratch); - scratch.llval - } - Ref(llval, align) => { - assert_eq!(align.abi(), op.layout.align.abi(), - "return place is unaligned!"); - llval - } - }; - bx.load( - bx.pointercast(llslot, cast_ty.llvm_type(bx.cx).ptr_to()), - self.fn_ty.ret.layout.align) - } - }; - bx.ret(llval); - } - - mir::TerminatorKind::Unreachable => { - bx.unreachable(); - } - - mir::TerminatorKind::Drop { ref location, target, unwind } => { - let ty = location.ty(self.mir, bx.tcx()).to_ty(bx.tcx()); - let ty = self.monomorphize(&ty); - let drop_fn = monomorphize::resolve_drop_in_place(bx.cx.tcx, ty); - - if let ty::InstanceDef::DropGlue(_, None) = drop_fn.def { - // we don't actually need to drop anything. - funclet_br(self, bx, target); - return - } - - let place = self.trans_place(&bx, location); - let mut args: &[_] = &[place.llval, place.llextra]; - args = &args[..1 + place.has_extra() as usize]; - let (drop_fn, fn_ty) = match ty.sty { - ty::TyDynamic(..) => { - let fn_ty = drop_fn.ty(bx.cx.tcx); - let sig = common::ty_fn_sig(bx.cx, fn_ty); - let sig = bx.tcx().normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let fn_ty = FnType::new_vtable(bx.cx, sig, &[]); - args = &args[..1]; - (meth::DESTRUCTOR.get_fn(&bx, place.llextra, &fn_ty), fn_ty) - } - _ => { - (callee::get_fn(bx.cx, drop_fn), - FnType::of_instance(bx.cx, &drop_fn)) - } - }; - do_call(self, bx, fn_ty, drop_fn, args, - Some((ReturnDest::Nothing, target)), - unwind); - } - - mir::TerminatorKind::Assert { ref cond, expected, ref msg, target, cleanup } => { - let cond = self.trans_operand(&bx, cond).immediate(); - let mut const_cond = common::const_to_opt_u128(cond, false).map(|c| c == 1); - - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - // NOTE: Unlike binops, negation doesn't have its own - // checked operation, just a comparison with the minimum - // value, so we have to check for the assert message. - if !bx.cx.check_overflow { - if let mir::interpret::EvalErrorKind::OverflowNeg = *msg { - const_cond = Some(expected); - } - } - - // Don't translate the panic block if success if known. - if const_cond == Some(expected) { - funclet_br(self, bx, target); - return; - } - - // Pass the condition through llvm.expect for branch hinting. - let expect = bx.cx.get_intrinsic(&"llvm.expect.i1"); - let cond = bx.call(expect, &[cond, C_bool(bx.cx, expected)], None); - - // Create the failure block and the conditional branch to it. - let lltarget = llblock(self, target); - let panic_block = self.new_block("panic"); - if expected { - bx.cond_br(cond, lltarget, panic_block.llbb()); - } else { - bx.cond_br(cond, panic_block.llbb(), lltarget); - } - - // After this point, bx is the block for the call to panic. - bx = panic_block; - self.set_debug_loc(&bx, terminator.source_info); - - // Get the location information. - let loc = bx.sess().codemap().lookup_char_pos(span.lo()); - let filename = Symbol::intern(&loc.file.name.to_string()).as_str(); - let filename = C_str_slice(bx.cx, filename); - let line = C_u32(bx.cx, loc.line as u32); - let col = C_u32(bx.cx, loc.col.to_usize() as u32 + 1); - let align = tcx.data_layout.aggregate_align - .max(tcx.data_layout.i32_align) - .max(tcx.data_layout.pointer_align); - - // Put together the arguments to the panic entry point. - let (lang_item, args) = match *msg { - EvalErrorKind::BoundsCheck { ref len, ref index } => { - let len = self.trans_operand(&mut bx, len).immediate(); - let index = self.trans_operand(&mut bx, index).immediate(); - - let file_line_col = C_struct(bx.cx, &[filename, line, col], false); - let file_line_col = consts::addr_of(bx.cx, - file_line_col, - align, - "panic_bounds_check_loc"); - (lang_items::PanicBoundsCheckFnLangItem, - vec![file_line_col, index, len]) - } - _ => { - let str = msg.description(); - let msg_str = Symbol::intern(str).as_str(); - let msg_str = C_str_slice(bx.cx, msg_str); - let msg_file_line_col = C_struct(bx.cx, - &[msg_str, filename, line, col], - false); - let msg_file_line_col = consts::addr_of(bx.cx, - msg_file_line_col, - align, - "panic_loc"); - (lang_items::PanicFnLangItem, - vec![msg_file_line_col]) - } - }; - - // Obtain the panic entry point. - let def_id = common::langcall(bx.tcx(), Some(span), "", lang_item); - let instance = ty::Instance::mono(bx.tcx(), def_id); - let fn_ty = FnType::of_instance(bx.cx, &instance); - let llfn = callee::get_fn(bx.cx, instance); - - // Translate the actual panic invoke/call. - do_call(self, bx, fn_ty, llfn, &args, None, cleanup); - } - - mir::TerminatorKind::DropAndReplace { .. } => { - bug!("undesugared DropAndReplace in trans: {:?}", terminator); - } - - mir::TerminatorKind::Call { ref func, ref args, ref destination, cleanup } => { - // Create the callee. This is a fn ptr or zero-sized and hence a kind of scalar. - let callee = self.trans_operand(&bx, func); - - let (instance, mut llfn) = match callee.layout.ty.sty { - ty::TyFnDef(def_id, substs) => { - (Some(ty::Instance::resolve(bx.cx.tcx, - ty::ParamEnv::reveal_all(), - def_id, - substs).unwrap()), - None) - } - ty::TyFnPtr(_) => { - (None, Some(callee.immediate())) - } - _ => bug!("{} is not callable", callee.layout.ty) - }; - let def = instance.map(|i| i.def); - let sig = callee.layout.ty.fn_sig(bx.tcx()); - let sig = bx.tcx().normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - let abi = sig.abi; - - // Handle intrinsics old trans wants Expr's for, ourselves. - let intrinsic = match def { - Some(ty::InstanceDef::Intrinsic(def_id)) - => Some(bx.tcx().item_name(def_id).as_str()), - _ => None - }; - let intrinsic = intrinsic.as_ref().map(|s| &s[..]); - - if intrinsic == Some("transmute") { - let &(ref dest, target) = destination.as_ref().unwrap(); - self.trans_transmute(&bx, &args[0], dest); - funclet_br(self, bx, target); - return; - } - - let extra_args = &args[sig.inputs().len()..]; - let extra_args = extra_args.iter().map(|op_arg| { - let op_ty = op_arg.ty(self.mir, bx.tcx()); - self.monomorphize(&op_ty) - }).collect::>(); - - let fn_ty = match def { - Some(ty::InstanceDef::Virtual(..)) => { - FnType::new_vtable(bx.cx, sig, &extra_args) - } - Some(ty::InstanceDef::DropGlue(_, None)) => { - // empty drop glue - a nop. - let &(_, target) = destination.as_ref().unwrap(); - funclet_br(self, bx, target); - return; - } - _ => FnType::new(bx.cx, sig, &extra_args) - }; - - // The arguments we'll be passing. Plus one to account for outptr, if used. - let arg_count = fn_ty.args.len() + fn_ty.ret.is_indirect() as usize; - let mut llargs = Vec::with_capacity(arg_count); - - // Prepare the return value destination - let ret_dest = if let Some((ref dest, _)) = *destination { - let is_intrinsic = intrinsic.is_some(); - self.make_return_dest(&bx, dest, &fn_ty.ret, &mut llargs, - is_intrinsic) - } else { - ReturnDest::Nothing - }; - - if intrinsic.is_some() && intrinsic != Some("drop_in_place") { - use intrinsic::trans_intrinsic_call; - - let dest = match ret_dest { - _ if fn_ty.ret.is_indirect() => llargs[0], - ReturnDest::Nothing => { - C_undef(fn_ty.ret.memory_ty(bx.cx).ptr_to()) - } - ReturnDest::IndirectOperand(dst, _) | - ReturnDest::Store(dst) => dst.llval, - ReturnDest::DirectOperand(_) => - bug!("Cannot use direct operand with an intrinsic call") - }; - - let args: Vec<_> = args.iter().enumerate().map(|(i, arg)| { - // The indices passed to simd_shuffle* in the - // third argument must be constant. This is - // checked by const-qualification, which also - // promotes any complex rvalues to constants. - if i == 2 && intrinsic.unwrap().starts_with("simd_shuffle") { - match *arg { - mir::Operand::Copy(_) | - mir::Operand::Move(_) => { - span_bug!(span, "shuffle indices must be constant"); - } - mir::Operand::Constant(ref constant) => { - let (llval, ty) = self.simd_shuffle_indices( - &bx, - constant, - ); - return OperandRef { - val: Immediate(llval), - layout: bx.cx.layout_of(ty) - }; - } - } - } - - self.trans_operand(&bx, arg) - }).collect(); - - - let callee_ty = instance.as_ref().unwrap().ty(bx.cx.tcx); - trans_intrinsic_call(&bx, callee_ty, &fn_ty, &args, dest, - terminator.source_info.span); - - if let ReturnDest::IndirectOperand(dst, _) = ret_dest { - self.store_return(&bx, ret_dest, &fn_ty.ret, dst.llval); - } - - if let Some((_, target)) = *destination { - funclet_br(self, bx, target); - } else { - bx.unreachable(); - } - - return; - } - - // Split the rust-call tupled arguments off. - let (first_args, untuple) = if abi == Abi::RustCall && !args.is_empty() { - let (tup, args) = args.split_last().unwrap(); - (args, Some(tup)) - } else { - (&args[..], None) - }; - - for (i, arg) in first_args.iter().enumerate() { - let mut op = self.trans_operand(&bx, arg); - if let (0, Some(ty::InstanceDef::Virtual(_, idx))) = (i, def) { - if let Pair(data_ptr, meta) = op.val { - llfn = Some(meth::VirtualIndex::from_index(idx) - .get_fn(&bx, meta, &fn_ty)); - llargs.push(data_ptr); - continue; - } - } - - // The callee needs to own the argument memory if we pass it - // by-ref, so make a local copy of non-immediate constants. - match (arg, op.val) { - (&mir::Operand::Copy(_), Ref(..)) | - (&mir::Operand::Constant(_), Ref(..)) => { - let tmp = PlaceRef::alloca(&bx, op.layout, "const"); - op.val.store(&bx, tmp); - op.val = Ref(tmp.llval, tmp.align); - } - _ => {} - } - - self.trans_argument(&bx, op, &mut llargs, &fn_ty.args[i]); - } - if let Some(tup) = untuple { - self.trans_arguments_untupled(&bx, tup, &mut llargs, - &fn_ty.args[first_args.len()..]) - } - - let fn_ptr = match (llfn, instance) { - (Some(llfn), _) => llfn, - (None, Some(instance)) => callee::get_fn(bx.cx, instance), - _ => span_bug!(span, "no llfn for call"), - }; - - do_call(self, bx, fn_ty, fn_ptr, &llargs, - destination.as_ref().map(|&(_, target)| (ret_dest, target)), - cleanup); - } - mir::TerminatorKind::GeneratorDrop | - mir::TerminatorKind::Yield { .. } => bug!("generator ops in trans"), - mir::TerminatorKind::FalseEdges { .. } | - mir::TerminatorKind::FalseUnwind { .. } => bug!("borrowck false edges in trans"), - } - } - - fn trans_argument(&mut self, - bx: &Builder<'a, 'tcx>, - op: OperandRef<'tcx>, - llargs: &mut Vec, - arg: &ArgType<'tcx, Ty<'tcx>>) { - // Fill padding with undef value, where applicable. - if let Some(ty) = arg.pad { - llargs.push(C_undef(ty.llvm_type(bx.cx))); - } - - if arg.is_ignore() { - return; - } - - if let PassMode::Pair(..) = arg.mode { - match op.val { - Pair(a, b) => { - llargs.push(a); - llargs.push(b); - return; - } - _ => bug!("trans_argument: {:?} invalid for pair arugment", op) - } - } - - // Force by-ref if we have to load through a cast pointer. - let (mut llval, align, by_ref) = match op.val { - Immediate(_) | Pair(..) => { - match arg.mode { - PassMode::Indirect(_) | PassMode::Cast(_) => { - let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); - op.val.store(bx, scratch); - (scratch.llval, scratch.align, true) - } - _ => { - (op.immediate_or_packed_pair(bx), arg.layout.align, false) - } - } - } - Ref(llval, align) => { - if arg.is_indirect() && align.abi() < arg.layout.align.abi() { - // `foo(packed.large_field)`. We can't pass the (unaligned) field directly. I - // think that ATM (Rust 1.16) we only pass temporaries, but we shouldn't - // have scary latent bugs around. - - let scratch = PlaceRef::alloca(bx, arg.layout, "arg"); - base::memcpy_ty(bx, scratch.llval, llval, op.layout, align, MemFlags::empty()); - (scratch.llval, scratch.align, true) - } else { - (llval, align, true) - } - } - }; - - if by_ref && !arg.is_indirect() { - // Have to load the argument, maybe while casting it. - if let PassMode::Cast(ty) = arg.mode { - llval = bx.load(bx.pointercast(llval, ty.llvm_type(bx.cx).ptr_to()), - align.min(arg.layout.align)); - } else { - // We can't use `PlaceRef::load` here because the argument - // may have a type we don't treat as immediate, but the ABI - // used for this call is passing it by-value. In that case, - // the load would just produce `OperandValue::Ref` instead - // of the `OperandValue::Immediate` we need for the call. - llval = bx.load(llval, align); - if let layout::Abi::Scalar(ref scalar) = arg.layout.abi { - if scalar.is_bool() { - bx.range_metadata(llval, 0..2); - } - } - // We store bools as i8 so we need to truncate to i1. - llval = base::to_immediate(bx, llval, arg.layout); - } - } - - llargs.push(llval); - } - - fn trans_arguments_untupled(&mut self, - bx: &Builder<'a, 'tcx>, - operand: &mir::Operand<'tcx>, - llargs: &mut Vec, - args: &[ArgType<'tcx, Ty<'tcx>>]) { - let tuple = self.trans_operand(bx, operand); - - // Handle both by-ref and immediate tuples. - if let Ref(llval, align) = tuple.val { - let tuple_ptr = PlaceRef::new_sized(llval, tuple.layout, align); - for i in 0..tuple.layout.fields.count() { - let field_ptr = tuple_ptr.project_field(bx, i); - self.trans_argument(bx, field_ptr.load(bx), llargs, &args[i]); - } - } else { - // If the tuple is immediate, the elements are as well. - for i in 0..tuple.layout.fields.count() { - let op = tuple.extract_field(bx, i); - self.trans_argument(bx, op, llargs, &args[i]); - } - } - } - - fn get_personality_slot(&mut self, bx: &Builder<'a, 'tcx>) -> PlaceRef<'tcx> { - let cx = bx.cx; - if let Some(slot) = self.personality_slot { - slot - } else { - let layout = cx.layout_of(cx.tcx.intern_tup(&[ - cx.tcx.mk_mut_ptr(cx.tcx.types.u8), - cx.tcx.types.i32 - ])); - let slot = PlaceRef::alloca(bx, layout, "personalityslot"); - self.personality_slot = Some(slot); - slot - } - } - - /// Return the landingpad wrapper around the given basic block - /// - /// No-op in MSVC SEH scheme. - fn landing_pad_to(&mut self, target_bb: mir::BasicBlock) -> BasicBlockRef { - if let Some(block) = self.landing_pads[target_bb] { - return block; - } - - let block = self.blocks[target_bb]; - let landing_pad = self.landing_pad_uncached(block); - self.landing_pads[target_bb] = Some(landing_pad); - landing_pad - } - - fn landing_pad_uncached(&mut self, target_bb: BasicBlockRef) -> BasicBlockRef { - if base::wants_msvc_seh(self.cx.sess()) { - span_bug!(self.mir.span, "landing pad was not inserted?") - } - - let bx = self.new_block("cleanup"); - - let llpersonality = self.cx.eh_personality(); - let llretty = self.landing_pad_type(); - let lp = bx.landing_pad(llretty, llpersonality, 1); - bx.set_cleanup(lp); - - let slot = self.get_personality_slot(&bx); - slot.storage_live(&bx); - Pair(bx.extract_value(lp, 0), bx.extract_value(lp, 1)).store(&bx, slot); - - bx.br(target_bb); - bx.llbb() - } - - fn landing_pad_type(&self) -> Type { - let cx = self.cx; - Type::struct_(cx, &[Type::i8p(cx), Type::i32(cx)], false) - } - - fn unreachable_block(&mut self) -> BasicBlockRef { - self.unreachable_block.unwrap_or_else(|| { - let bl = self.new_block("unreachable"); - bl.unreachable(); - self.unreachable_block = Some(bl.llbb()); - bl.llbb() - }) - } - - pub fn new_block(&self, name: &str) -> Builder<'a, 'tcx> { - Builder::new_block(self.cx, self.llfn, name) - } - - pub fn build_block(&self, bb: mir::BasicBlock) -> Builder<'a, 'tcx> { - let bx = Builder::with_cx(self.cx); - bx.position_at_end(self.blocks[bb]); - bx - } - - fn make_return_dest(&mut self, bx: &Builder<'a, 'tcx>, - dest: &mir::Place<'tcx>, fn_ret: &ArgType<'tcx, Ty<'tcx>>, - llargs: &mut Vec, is_intrinsic: bool) - -> ReturnDest<'tcx> { - // If the return is ignored, we can just return a do-nothing ReturnDest - if fn_ret.is_ignore() { - return ReturnDest::Nothing; - } - let dest = if let mir::Place::Local(index) = *dest { - match self.locals[index] { - LocalRef::Place(dest) => dest, - LocalRef::Operand(None) => { - // Handle temporary places, specifically Operand ones, as - // they don't have allocas - return if fn_ret.is_indirect() { - // Odd, but possible, case, we have an operand temporary, - // but the calling convention has an indirect return. - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - llargs.push(tmp.llval); - ReturnDest::IndirectOperand(tmp, index) - } else if is_intrinsic { - // Currently, intrinsics always need a location to store - // the result. so we create a temporary alloca for the - // result - let tmp = PlaceRef::alloca(bx, fn_ret.layout, "tmp_ret"); - tmp.storage_live(bx); - ReturnDest::IndirectOperand(tmp, index) - } else { - ReturnDest::DirectOperand(index) - }; - } - LocalRef::Operand(Some(_)) => { - bug!("place local already assigned to"); - } - } - } else { - self.trans_place(bx, dest) - }; - if fn_ret.is_indirect() { - if dest.align.abi() < dest.layout.align.abi() { - // Currently, MIR code generation does not create calls - // that store directly to fields of packed structs (in - // fact, the calls it creates write only to temps), - // - // If someone changes that, please update this code path - // to create a temporary. - span_bug!(self.mir.span, "can't directly store to unaligned value"); - } - llargs.push(dest.llval); - ReturnDest::Nothing - } else { - ReturnDest::Store(dest) - } - } - - fn trans_transmute(&mut self, bx: &Builder<'a, 'tcx>, - src: &mir::Operand<'tcx>, - dst: &mir::Place<'tcx>) { - if let mir::Place::Local(index) = *dst { - match self.locals[index] { - LocalRef::Place(place) => self.trans_transmute_into(bx, src, place), - LocalRef::Operand(None) => { - let dst_layout = bx.cx.layout_of(self.monomorphized_place_ty(dst)); - assert!(!dst_layout.ty.has_erasable_regions()); - let place = PlaceRef::alloca(bx, dst_layout, "transmute_temp"); - place.storage_live(bx); - self.trans_transmute_into(bx, src, place); - let op = place.load(bx); - place.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - } - LocalRef::Operand(Some(op)) => { - assert!(op.layout.is_zst(), - "assigning to initialized SSAtemp"); - } - } - } else { - let dst = self.trans_place(bx, dst); - self.trans_transmute_into(bx, src, dst); - } - } - - fn trans_transmute_into(&mut self, bx: &Builder<'a, 'tcx>, - src: &mir::Operand<'tcx>, - dst: PlaceRef<'tcx>) { - let src = self.trans_operand(bx, src); - let llty = src.layout.llvm_type(bx.cx); - let cast_ptr = bx.pointercast(dst.llval, llty.ptr_to()); - let align = src.layout.align.min(dst.layout.align); - src.val.store(bx, PlaceRef::new_sized(cast_ptr, src.layout, align)); - } - - - // Stores the return value of a function call into it's final location. - fn store_return(&mut self, - bx: &Builder<'a, 'tcx>, - dest: ReturnDest<'tcx>, - ret_ty: &ArgType<'tcx, Ty<'tcx>>, - llval: ValueRef) { - use self::ReturnDest::*; - - match dest { - Nothing => (), - Store(dst) => ret_ty.store(bx, llval, dst), - IndirectOperand(tmp, index) => { - let op = tmp.load(bx); - tmp.storage_dead(bx); - self.locals[index] = LocalRef::Operand(Some(op)); - } - DirectOperand(index) => { - // If there is a cast, we have to store and reload. - let op = if let PassMode::Cast(_) = ret_ty.mode { - let tmp = PlaceRef::alloca(bx, ret_ty.layout, "tmp_ret"); - tmp.storage_live(bx); - ret_ty.store(bx, llval, tmp); - let op = tmp.load(bx); - tmp.storage_dead(bx); - op - } else { - OperandRef::from_immediate_or_packed_pair(bx, llval, ret_ty.layout) - }; - self.locals[index] = LocalRef::Operand(Some(op)); - } - } - } -} - -enum ReturnDest<'tcx> { - // Do nothing, the return value is indirect or ignored - Nothing, - // Store the return value to the pointer - Store(PlaceRef<'tcx>), - // Stores an indirect return value to an operand local place - IndirectOperand(PlaceRef<'tcx>, mir::Local), - // Stores a direct return value to an operand local place - DirectOperand(mir::Local) -} diff --git a/src/librustc_trans/mir/constant.rs b/src/librustc_trans/mir/constant.rs deleted file mode 100644 index a10b7c9c9f1..00000000000 --- a/src/librustc_trans/mir/constant.rs +++ /dev/null @@ -1,271 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::middle::const_val::{ConstVal, ConstEvalErr}; -use rustc_mir::interpret::{read_target_uint, const_val_field}; -use rustc::hir::def_id::DefId; -use rustc::mir; -use rustc_data_structures::indexed_vec::Idx; -use rustc::mir::interpret::{GlobalId, MemoryPointer, PrimVal, Allocation, ConstValue}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, HasDataLayout, LayoutOf, Scalar}; -use builder::Builder; -use common::{CodegenCx}; -use common::{C_bytes, C_struct, C_uint_big, C_undef, C_usize}; -use consts; -use type_of::LayoutLlvmExt; -use type_::Type; -use syntax::ast::Mutability; - -use super::super::callee; -use super::FunctionCx; - -pub fn primval_to_llvm(cx: &CodegenCx, - cv: PrimVal, - scalar: &Scalar, - llty: Type) -> ValueRef { - let bits = if scalar.is_bool() { 1 } else { scalar.value.size(cx).bits() }; - match cv { - PrimVal::Undef => C_undef(Type::ix(cx, bits)), - PrimVal::Bytes(b) => { - let llval = C_uint_big(Type::ix(cx, bits), b); - if scalar.value == layout::Pointer { - unsafe { llvm::LLVMConstIntToPtr(llval, llty.to_ref()) } - } else { - consts::bitcast(llval, llty) - } - }, - PrimVal::Ptr(ptr) => { - if let Some(fn_instance) = cx.tcx.interpret_interner.get_fn(ptr.alloc_id) { - callee::get_fn(cx, fn_instance) - } else { - let static_ = cx - .tcx - .interpret_interner - .get_static(ptr.alloc_id); - let base_addr = if let Some(def_id) = static_ { - assert!(cx.tcx.is_static(def_id).is_some()); - consts::get_static(cx, def_id) - } else if let Some(alloc) = cx.tcx.interpret_interner - .get_alloc(ptr.alloc_id) { - let init = const_alloc_to_llvm(cx, alloc); - if alloc.runtime_mutability == Mutability::Mutable { - consts::addr_of_mut(cx, init, alloc.align, "byte_str") - } else { - consts::addr_of(cx, init, alloc.align, "byte_str") - } - } else { - bug!("missing allocation {:?}", ptr.alloc_id); - }; - - let llval = unsafe { llvm::LLVMConstInBoundsGEP( - consts::bitcast(base_addr, Type::i8p(cx)), - &C_usize(cx, ptr.offset), - 1, - ) }; - if scalar.value != layout::Pointer { - unsafe { llvm::LLVMConstPtrToInt(llval, llty.to_ref()) } - } else { - consts::bitcast(llval, llty) - } - } - } - } -} - -fn const_value_to_llvm<'tcx>(cx: &CodegenCx<'_, 'tcx>, val: ConstValue, ty: Ty<'tcx>) -> ValueRef { - let layout = cx.layout_of(ty); - - if layout.is_zst() { - return C_undef(layout.immediate_llvm_type(cx)); - } - - match val { - ConstValue::ByVal(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("const_value_to_llvm: invalid ByVal layout: {:#?}", layout) - }; - primval_to_llvm( - cx, - x, - scalar, - layout.immediate_llvm_type(cx), - ) - }, - ConstValue::ByValPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("const_value_to_llvm: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(cx, 0), - ); - let b_llval = primval_to_llvm( - cx, - b, - b_scalar, - layout.scalar_pair_element_llvm_type(cx, 1), - ); - C_struct(cx, &[a_llval, b_llval], false) - }, - ConstValue::ByRef(alloc) => const_alloc_to_llvm(cx, alloc), - } -} - -pub fn const_alloc_to_llvm(cx: &CodegenCx, alloc: &Allocation) -> ValueRef { - let mut llvals = Vec::with_capacity(alloc.relocations.len() + 1); - let layout = cx.data_layout(); - let pointer_size = layout.pointer_size.bytes() as usize; - - let mut next_offset = 0; - for (&offset, &alloc_id) in &alloc.relocations { - assert_eq!(offset as usize as u64, offset); - let offset = offset as usize; - if offset > next_offset { - llvals.push(C_bytes(cx, &alloc.bytes[next_offset..offset])); - } - let ptr_offset = read_target_uint( - layout.endian, - &alloc.bytes[offset..(offset + pointer_size)], - ).expect("const_alloc_to_llvm: could not read relocation pointer") as u64; - llvals.push(primval_to_llvm( - cx, - PrimVal::Ptr(MemoryPointer { alloc_id, offset: ptr_offset }), - &Scalar { - value: layout::Primitive::Pointer, - valid_range: 0..=!0 - }, - Type::i8p(cx) - )); - next_offset = offset + pointer_size; - } - if alloc.bytes.len() >= next_offset { - llvals.push(C_bytes(cx, &alloc.bytes[next_offset ..])); - } - - C_struct(cx, &llvals, true) -} - -pub fn trans_static_initializer<'a, 'tcx>( - cx: &CodegenCx<'a, 'tcx>, - def_id: DefId) - -> Result> -{ - let instance = ty::Instance::mono(cx.tcx, def_id); - let cid = GlobalId { - instance, - promoted: None - }; - let param_env = ty::ParamEnv::reveal_all(); - let static_ = cx.tcx.const_eval(param_env.and(cid))?; - - let val = match static_.val { - ConstVal::Value(val) => val, - _ => bug!("static const eval returned {:#?}", static_), - }; - Ok(const_value_to_llvm(cx, val, static_.ty)) -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - fn const_to_const_value( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &'tcx ty::Const<'tcx>, - ) -> Result, ConstEvalErr<'tcx>> { - match constant.val { - ConstVal::Unevaluated(def_id, ref substs) => { - let tcx = bx.tcx(); - let param_env = ty::ParamEnv::reveal_all(); - let instance = ty::Instance::resolve(tcx, param_env, def_id, substs).unwrap(); - let cid = GlobalId { - instance, - promoted: None, - }; - let c = tcx.const_eval(param_env.and(cid))?; - self.const_to_const_value(bx, c) - }, - ConstVal::Value(val) => Ok(val), - } - } - - pub fn mir_constant_to_const_value( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>, - ) -> Result, ConstEvalErr<'tcx>> { - match constant.literal { - mir::Literal::Promoted { index } => { - let param_env = ty::ParamEnv::reveal_all(); - let cid = mir::interpret::GlobalId { - instance: self.instance, - promoted: Some(index), - }; - bx.tcx().const_eval(param_env.and(cid)) - } - mir::Literal::Value { value } => { - Ok(self.monomorphize(&value)) - } - }.and_then(|c| self.const_to_const_value(bx, c)) - } - - /// process constant containing SIMD shuffle indices - pub fn simd_shuffle_indices( - &mut self, - bx: &Builder<'a, 'tcx>, - constant: &mir::Constant<'tcx>, - ) -> (ValueRef, Ty<'tcx>) { - self.mir_constant_to_const_value(bx, constant) - .and_then(|c| { - let field_ty = constant.ty.builtin_index().unwrap(); - let fields = match constant.ty.sty { - ty::TyArray(_, n) => n.unwrap_usize(bx.tcx()), - ref other => bug!("invalid simd shuffle type: {}", other), - }; - let values: Result, _> = (0..fields).map(|field| { - let field = const_val_field( - bx.tcx(), - ty::ParamEnv::reveal_all(), - self.instance, - None, - mir::Field::new(field as usize), - c, - constant.ty, - )?; - if let Some(prim) = field.to_primval() { - let layout = bx.cx.layout_of(field_ty); - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - Ok(primval_to_llvm( - bx.cx, prim, scalar, - layout.immediate_llvm_type(bx.cx), - )) - } else { - bug!("simd shuffle field {:?}", field) - } - }).collect(); - let llval = C_struct(bx.cx, &values?, false); - Ok((llval, constant.ty)) - }) - .unwrap_or_else(|e| { - e.report(bx.tcx(), constant.span, "shuffle_indices"); - // We've errored, so we don't have to produce working code. - let ty = self.monomorphize(&constant.ty); - let llty = bx.cx.layout_of(ty).llvm_type(bx.cx); - (C_undef(llty), ty) - }) - } -} diff --git a/src/librustc_trans/mir/mod.rs b/src/librustc_trans/mir/mod.rs deleted file mode 100644 index 8ea0983075d..00000000000 --- a/src/librustc_trans/mir/mod.rs +++ /dev/null @@ -1,652 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use common::{C_i32, C_null}; -use libc::c_uint; -use llvm::{self, ValueRef, BasicBlockRef}; -use llvm::debuginfo::DIScope; -use rustc::ty::{self, Ty, TypeFoldable, UpvarSubsts}; -use rustc::ty::layout::{LayoutOf, TyLayout}; -use rustc::mir::{self, Mir}; -use rustc::ty::subst::Substs; -use rustc::session::config::FullDebugInfo; -use base; -use builder::Builder; -use common::{CodegenCx, Funclet}; -use debuginfo::{self, declare_local, VariableAccess, VariableKind, FunctionDebugContext}; -use monomorphize::Instance; -use abi::{ArgAttribute, ArgTypeExt, FnType, FnTypeExt, PassMode}; -use type_::Type; - -use syntax_pos::{DUMMY_SP, NO_EXPANSION, BytePos, Span}; -use syntax::symbol::keywords; - -use std::iter; - -use rustc_data_structures::bitvec::BitVector; -use rustc_data_structures::indexed_vec::{IndexVec, Idx}; - -pub use self::constant::trans_static_initializer; - -use self::analyze::CleanupKind; -use self::place::PlaceRef; -use rustc::mir::traversal; - -use self::operand::{OperandRef, OperandValue}; - -/// Master context for translating MIR. -pub struct FunctionCx<'a, 'tcx:'a> { - instance: Instance<'tcx>, - - mir: &'a mir::Mir<'tcx>, - - debug_context: debuginfo::FunctionDebugContext, - - llfn: ValueRef, - - cx: &'a CodegenCx<'a, 'tcx>, - - fn_ty: FnType<'tcx, Ty<'tcx>>, - - /// When unwinding is initiated, we have to store this personality - /// value somewhere so that we can load it and re-use it in the - /// resume instruction. The personality is (afaik) some kind of - /// value used for C++ unwinding, which must filter by type: we - /// don't really care about it very much. Anyway, this value - /// contains an alloca into which the personality is stored and - /// then later loaded when generating the DIVERGE_BLOCK. - personality_slot: Option>, - - /// A `Block` for each MIR `BasicBlock` - blocks: IndexVec, - - /// The funclet status of each basic block - cleanup_kinds: IndexVec, - - /// When targeting MSVC, this stores the cleanup info for each funclet - /// BB. This is initialized as we compute the funclets' head block in RPO. - funclets: &'a IndexVec>, - - /// This stores the landing-pad block for a given BB, computed lazily on GNU - /// and eagerly on MSVC. - landing_pads: IndexVec>, - - /// Cached unreachable block - unreachable_block: Option, - - /// The location where each MIR arg/var/tmp/ret is stored. This is - /// usually an `PlaceRef` representing an alloca, but not always: - /// sometimes we can skip the alloca and just store the value - /// directly using an `OperandRef`, which makes for tighter LLVM - /// IR. The conditions for using an `OperandRef` are as follows: - /// - /// - the type of the local must be judged "immediate" by `is_llvm_immediate` - /// - the operand must never be referenced indirectly - /// - we should not take its address using the `&` operator - /// - nor should it appear in a place path like `tmp.a` - /// - the operand must be defined by an rvalue that can generate immediate - /// values - /// - /// Avoiding allocs can also be important for certain intrinsics, - /// notably `expect`. - locals: IndexVec>, - - /// Debug information for MIR scopes. - scopes: IndexVec, - - /// If this function is being monomorphized, this contains the type substitutions used. - param_substs: &'tcx Substs<'tcx>, -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn monomorphize(&self, value: &T) -> T - where T: TypeFoldable<'tcx> - { - self.cx.tcx.subst_and_normalize_erasing_regions( - self.param_substs, - ty::ParamEnv::reveal_all(), - value, - ) - } - - pub fn set_debug_loc(&mut self, bx: &Builder, source_info: mir::SourceInfo) { - let (scope, span) = self.debug_loc(source_info); - debuginfo::set_source_location(&self.debug_context, bx, scope, span); - } - - pub fn debug_loc(&mut self, source_info: mir::SourceInfo) -> (DIScope, Span) { - // Bail out if debug info emission is not enabled. - match self.debug_context { - FunctionDebugContext::DebugInfoDisabled | - FunctionDebugContext::FunctionWithoutDebugInfo => { - return (self.scopes[source_info.scope].scope_metadata, source_info.span); - } - FunctionDebugContext::RegularContext(_) =>{} - } - - // In order to have a good line stepping behavior in debugger, we overwrite debug - // locations of macro expansions with that of the outermost expansion site - // (unless the crate is being compiled with `-Z debug-macros`). - if source_info.span.ctxt() == NO_EXPANSION || - self.cx.sess().opts.debugging_opts.debug_macros { - let scope = self.scope_metadata_for_loc(source_info.scope, source_info.span.lo()); - (scope, source_info.span) - } else { - // Walk up the macro expansion chain until we reach a non-expanded span. - // We also stop at the function body level because no line stepping can occur - // at the level above that. - let mut span = source_info.span; - while span.ctxt() != NO_EXPANSION && span.ctxt() != self.mir.span.ctxt() { - if let Some(info) = span.ctxt().outer().expn_info() { - span = info.call_site; - } else { - break; - } - } - let scope = self.scope_metadata_for_loc(source_info.scope, span.lo()); - // Use span of the outermost expansion site, while keeping the original lexical scope. - (scope, span) - } - } - - // DILocations inherit source file name from the parent DIScope. Due to macro expansions - // it may so happen that the current span belongs to a different file than the DIScope - // corresponding to span's containing visibility scope. If so, we need to create a DIScope - // "extension" into that file. - fn scope_metadata_for_loc(&self, scope_id: mir::VisibilityScope, pos: BytePos) - -> llvm::debuginfo::DIScope { - let scope_metadata = self.scopes[scope_id].scope_metadata; - if pos < self.scopes[scope_id].file_start_pos || - pos >= self.scopes[scope_id].file_end_pos { - let cm = self.cx.sess().codemap(); - let defining_crate = self.debug_context.get_ref(DUMMY_SP).defining_crate; - debuginfo::extend_scope_to_file(self.cx, - scope_metadata, - &cm.lookup_char_pos(pos).file, - defining_crate) - } else { - scope_metadata - } - } -} - -enum LocalRef<'tcx> { - Place(PlaceRef<'tcx>), - Operand(Option>), -} - -impl<'a, 'tcx> LocalRef<'tcx> { - fn new_operand(cx: &CodegenCx<'a, 'tcx>, layout: TyLayout<'tcx>) -> LocalRef<'tcx> { - if layout.is_zst() { - // Zero-size temporaries aren't always initialized, which - // doesn't matter because they don't contain data, but - // we need something in the operand. - LocalRef::Operand(Some(OperandRef::new_zst(cx, layout))) - } else { - LocalRef::Operand(None) - } - } -} - -/////////////////////////////////////////////////////////////////////////// - -pub fn trans_mir<'a, 'tcx: 'a>( - cx: &'a CodegenCx<'a, 'tcx>, - llfn: ValueRef, - mir: &'a Mir<'tcx>, - instance: Instance<'tcx>, - sig: ty::FnSig<'tcx>, -) { - let fn_ty = FnType::new(cx, sig, &[]); - debug!("fn_ty: {:?}", fn_ty); - let debug_context = - debuginfo::create_function_debug_context(cx, instance, sig, llfn, mir); - let bx = Builder::new_block(cx, llfn, "start"); - - if mir.basic_blocks().iter().any(|bb| bb.is_cleanup) { - bx.set_personality_fn(cx.eh_personality()); - } - - let cleanup_kinds = analyze::cleanup_kinds(&mir); - // Allocate a `Block` for every basic block, except - // the start block, if nothing loops back to it. - let reentrant_start_block = !mir.predecessors_for(mir::START_BLOCK).is_empty(); - let block_bxs: IndexVec = - mir.basic_blocks().indices().map(|bb| { - if bb == mir::START_BLOCK && !reentrant_start_block { - bx.llbb() - } else { - bx.build_sibling_block(&format!("{:?}", bb)).llbb() - } - }).collect(); - - // Compute debuginfo scopes from MIR scopes. - let scopes = debuginfo::create_mir_scopes(cx, mir, &debug_context); - let (landing_pads, funclets) = create_funclets(mir, &bx, &cleanup_kinds, &block_bxs); - - let mut fx = FunctionCx { - instance, - mir, - llfn, - fn_ty, - cx, - personality_slot: None, - blocks: block_bxs, - unreachable_block: None, - cleanup_kinds, - landing_pads, - funclets: &funclets, - scopes, - locals: IndexVec::new(), - debug_context, - param_substs: { - assert!(!instance.substs.needs_infer()); - instance.substs - }, - }; - - let memory_locals = analyze::non_ssa_locals(&fx); - - // Allocate variable and temp allocas - fx.locals = { - let args = arg_local_refs(&bx, &fx, &fx.scopes, &memory_locals); - - let mut allocate_local = |local| { - let decl = &mir.local_decls[local]; - let layout = bx.cx.layout_of(fx.monomorphize(&decl.ty)); - assert!(!layout.ty.has_erasable_regions()); - - if let Some(name) = decl.name { - // User variable - let debug_scope = fx.scopes[decl.source_info.scope]; - let dbg = debug_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo; - - if !memory_locals.contains(local.index()) && !dbg { - debug!("alloc: {:?} ({}) -> operand", local, name); - return LocalRef::new_operand(bx.cx, layout); - } - - debug!("alloc: {:?} ({}) -> place", local, name); - let place = PlaceRef::alloca(&bx, layout, &name.as_str()); - if dbg { - let (scope, span) = fx.debug_loc(decl.source_info); - declare_local(&bx, &fx.debug_context, name, layout.ty, scope, - VariableAccess::DirectVariable { alloca: place.llval }, - VariableKind::LocalVariable, span); - } - LocalRef::Place(place) - } else { - // Temporary or return place - if local == mir::RETURN_PLACE && fx.fn_ty.ret.is_indirect() { - debug!("alloc: {:?} (return place) -> place", local); - let llretptr = llvm::get_param(llfn, 0); - LocalRef::Place(PlaceRef::new_sized(llretptr, layout, layout.align)) - } else if memory_locals.contains(local.index()) { - debug!("alloc: {:?} -> place", local); - LocalRef::Place(PlaceRef::alloca(&bx, layout, &format!("{:?}", local))) - } else { - // If this is an immediate local, we do not create an - // alloca in advance. Instead we wait until we see the - // definition and update the operand there. - debug!("alloc: {:?} -> operand", local); - LocalRef::new_operand(bx.cx, layout) - } - } - }; - - let retptr = allocate_local(mir::RETURN_PLACE); - iter::once(retptr) - .chain(args.into_iter()) - .chain(mir.vars_and_temps_iter().map(allocate_local)) - .collect() - }; - - // Branch to the START block, if it's not the entry block. - if reentrant_start_block { - bx.br(fx.blocks[mir::START_BLOCK]); - } - - // Up until here, IR instructions for this function have explicitly not been annotated with - // source code location, so we don't step into call setup code. From here on, source location - // emitting should be enabled. - debuginfo::start_emitting_source_locations(&fx.debug_context); - - let rpo = traversal::reverse_postorder(&mir); - let mut visited = BitVector::new(mir.basic_blocks().len()); - - // Translate the body of each block using reverse postorder - for (bb, _) in rpo { - visited.insert(bb.index()); - fx.trans_block(bb); - } - - // Remove blocks that haven't been visited, or have no - // predecessors. - for bb in mir.basic_blocks().indices() { - // Unreachable block - if !visited.contains(bb.index()) { - debug!("trans_mir: block {:?} was not visited", bb); - unsafe { - llvm::LLVMDeleteBasicBlock(fx.blocks[bb]); - } - } - } -} - -fn create_funclets<'a, 'tcx>( - mir: &'a Mir<'tcx>, - bx: &Builder<'a, 'tcx>, - cleanup_kinds: &IndexVec, - block_bxs: &IndexVec) - -> (IndexVec>, - IndexVec>) -{ - block_bxs.iter_enumerated().zip(cleanup_kinds).map(|((bb, &llbb), cleanup_kind)| { - match *cleanup_kind { - CleanupKind::Funclet if base::wants_msvc_seh(bx.sess()) => {} - _ => return (None, None) - } - - let cleanup; - let ret_llbb; - match mir[bb].terminator.as_ref().map(|t| &t.kind) { - // This is a basic block that we're aborting the program for, - // notably in an `extern` function. These basic blocks are inserted - // so that we assert that `extern` functions do indeed not panic, - // and if they do we abort the process. - // - // On MSVC these are tricky though (where we're doing funclets). If - // we were to do a cleanuppad (like below) the normal functions like - // `longjmp` would trigger the abort logic, terminating the - // program. Instead we insert the equivalent of `catch(...)` for C++ - // which magically doesn't trigger when `longjmp` files over this - // frame. - // - // Lots more discussion can be found on #48251 but this codegen is - // modeled after clang's for: - // - // try { - // foo(); - // } catch (...) { - // bar(); - // } - Some(&mir::TerminatorKind::Abort) => { - let cs_bx = bx.build_sibling_block(&format!("cs_funclet{:?}", bb)); - let cp_bx = bx.build_sibling_block(&format!("cp_funclet{:?}", bb)); - ret_llbb = cs_bx.llbb(); - - let cs = cs_bx.catch_switch(None, None, 1); - cs_bx.add_handler(cs, cp_bx.llbb()); - - // The "null" here is actually a RTTI type descriptor for the - // C++ personality function, but `catch (...)` has no type so - // it's null. The 64 here is actually a bitfield which - // represents that this is a catch-all block. - let null = C_null(Type::i8p(bx.cx)); - let sixty_four = C_i32(bx.cx, 64); - cleanup = cp_bx.catch_pad(cs, &[null, sixty_four, null]); - cp_bx.br(llbb); - } - _ => { - let cleanup_bx = bx.build_sibling_block(&format!("funclet_{:?}", bb)); - ret_llbb = cleanup_bx.llbb(); - cleanup = cleanup_bx.cleanup_pad(None, &[]); - cleanup_bx.br(llbb); - } - }; - - (Some(ret_llbb), Some(Funclet::new(cleanup))) - }).unzip() -} - -/// Produce, for each argument, a `ValueRef` pointing at the -/// argument's value. As arguments are places, these are always -/// indirect. -fn arg_local_refs<'a, 'tcx>(bx: &Builder<'a, 'tcx>, - fx: &FunctionCx<'a, 'tcx>, - scopes: &IndexVec, - memory_locals: &BitVector) - -> Vec> { - let mir = fx.mir; - let tcx = bx.tcx(); - let mut idx = 0; - let mut llarg_idx = fx.fn_ty.ret.is_indirect() as usize; - - // Get the argument scope, if it exists and if we need it. - let arg_scope = scopes[mir::ARGUMENT_VISIBILITY_SCOPE]; - let arg_scope = if arg_scope.is_valid() && bx.sess().opts.debuginfo == FullDebugInfo { - Some(arg_scope.scope_metadata) - } else { - None - }; - - let deref_op = unsafe { - [llvm::LLVMRustDIBuilderCreateOpDeref()] - }; - - mir.args_iter().enumerate().map(|(arg_index, local)| { - let arg_decl = &mir.local_decls[local]; - - let name = if let Some(name) = arg_decl.name { - name.as_str().to_string() - } else { - format!("arg{}", arg_index) - }; - - if Some(local) == mir.spread_arg { - // This argument (e.g. the last argument in the "rust-call" ABI) - // is a tuple that was spread at the ABI level and now we have - // to reconstruct it into a tuple local variable, from multiple - // individual LLVM function arguments. - - let arg_ty = fx.monomorphize(&arg_decl.ty); - let tupled_arg_tys = match arg_ty.sty { - ty::TyTuple(ref tys) => tys, - _ => bug!("spread argument isn't a tuple?!") - }; - - let place = PlaceRef::alloca(bx, bx.cx.layout_of(arg_ty), &name); - for i in 0..tupled_arg_tys.len() { - let arg = &fx.fn_ty.args[idx]; - idx += 1; - if arg.pad.is_some() { - llarg_idx += 1; - } - arg.store_fn_arg(bx, &mut llarg_idx, place.project_field(bx, i)); - } - - // Now that we have one alloca that contains the aggregate value, - // we can create one debuginfo entry for the argument. - arg_scope.map(|scope| { - let variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - declare_local( - bx, - &fx.debug_context, - arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg_ty, scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - }); - - return LocalRef::Place(place); - } - - let arg = &fx.fn_ty.args[idx]; - idx += 1; - if arg.pad.is_some() { - llarg_idx += 1; - } - - if arg_scope.is_none() && !memory_locals.contains(local.index()) { - // We don't have to cast or keep the argument in the alloca. - // FIXME(eddyb): We should figure out how to use llvm.dbg.value instead - // of putting everything in allocas just so we can use llvm.dbg.declare. - let local = |op| LocalRef::Operand(Some(op)); - match arg.mode { - PassMode::Ignore => { - return local(OperandRef::new_zst(bx.cx, arg.layout)); - } - PassMode::Direct(_) => { - let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(llarg, &name); - llarg_idx += 1; - return local( - OperandRef::from_immediate_or_packed_pair(bx, llarg, arg.layout)); - } - PassMode::Pair(..) => { - let a = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(a, &(name.clone() + ".0")); - llarg_idx += 1; - - let b = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(b, &(name + ".1")); - llarg_idx += 1; - - return local(OperandRef { - val: OperandValue::Pair(a, b), - layout: arg.layout - }); - } - _ => {} - } - } - - let place = if arg.is_indirect() { - // Don't copy an indirect argument to an alloca, the caller - // already put it in a temporary alloca and gave it up. - // FIXME: lifetimes - let llarg = llvm::get_param(bx.llfn(), llarg_idx as c_uint); - bx.set_value_name(llarg, &name); - llarg_idx += 1; - PlaceRef::new_sized(llarg, arg.layout, arg.layout.align) - } else { - let tmp = PlaceRef::alloca(bx, arg.layout, &name); - arg.store_fn_arg(bx, &mut llarg_idx, tmp); - tmp - }; - arg_scope.map(|scope| { - // Is this a regular argument? - if arg_index > 0 || mir.upvar_decls.is_empty() { - // The Rust ABI passes indirect variables using a pointer and a manual copy, so we - // need to insert a deref here, but the C ABI uses a pointer and a copy using the - // byval attribute, for which LLVM does the deref itself, so we must not add it. - // Starting with D31439 in LLVM 5, it *always* does the deref itself. - let mut variable_access = VariableAccess::DirectVariable { - alloca: place.llval - }; - if unsafe { llvm::LLVMRustVersionMajor() < 5 } { - if let PassMode::Indirect(ref attrs) = arg.mode { - if !attrs.contains(ArgAttribute::ByVal) { - variable_access = VariableAccess::IndirectVariable { - alloca: place.llval, - address_operations: &deref_op, - }; - } - } - } - - declare_local( - bx, - &fx.debug_context, - arg_decl.name.unwrap_or(keywords::Invalid.name()), - arg.layout.ty, - scope, - variable_access, - VariableKind::ArgumentVariable(arg_index + 1), - DUMMY_SP - ); - return; - } - - // Or is it the closure environment? - let (closure_layout, env_ref) = match arg.layout.ty.sty { - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) | - ty::TyRef(_, ty, _) => (bx.cx.layout_of(ty), true), - _ => (arg.layout, false) - }; - - let (def_id, upvar_substs) = match closure_layout.ty.sty { - ty::TyClosure(def_id, substs) => (def_id, UpvarSubsts::Closure(substs)), - ty::TyGenerator(def_id, substs, _) => (def_id, UpvarSubsts::Generator(substs)), - _ => bug!("upvar_decls with non-closure arg0 type `{}`", closure_layout.ty) - }; - let upvar_tys = upvar_substs.upvar_tys(def_id, tcx); - - // Store the pointer to closure data in an alloca for debuginfo - // because that's what the llvm.dbg.declare intrinsic expects. - - // FIXME(eddyb) this shouldn't be necessary but SROA seems to - // mishandle DW_OP_plus not preceded by DW_OP_deref, i.e. it - // doesn't actually strip the offset when splitting the closure - // environment into its components so it ends up out of bounds. - let env_ptr = if !env_ref { - let scratch = PlaceRef::alloca(bx, - bx.cx.layout_of(tcx.mk_mut_ptr(arg.layout.ty)), - "__debuginfo_env_ptr"); - bx.store(place.llval, scratch.llval, scratch.align); - scratch.llval - } else { - place.llval - }; - - for (i, (decl, ty)) in mir.upvar_decls.iter().zip(upvar_tys).enumerate() { - let byte_offset_of_var_in_env = closure_layout.fields.offset(i).bytes(); - - let ops = unsafe { - [llvm::LLVMRustDIBuilderCreateOpDeref(), - llvm::LLVMRustDIBuilderCreateOpPlusUconst(), - byte_offset_of_var_in_env as i64, - llvm::LLVMRustDIBuilderCreateOpDeref()] - }; - - // The environment and the capture can each be indirect. - - // FIXME(eddyb) see above why we have to keep - // a pointer in an alloca for debuginfo atm. - let mut ops = if env_ref || true { &ops[..] } else { &ops[1..] }; - - let ty = if let (true, &ty::TyRef(_, ty, _)) = (decl.by_ref, &ty.sty) { - ty - } else { - ops = &ops[..ops.len() - 1]; - ty - }; - - let variable_access = VariableAccess::IndirectVariable { - alloca: env_ptr, - address_operations: &ops - }; - declare_local( - bx, - &fx.debug_context, - decl.debug_name, - ty, - scope, - variable_access, - VariableKind::CapturedVariable, - DUMMY_SP - ); - } - }); - LocalRef::Place(place) - }).collect() -} - -mod analyze; -mod block; -mod constant; -pub mod place; -pub mod operand; -mod rvalue; -mod statement; diff --git a/src/librustc_trans/mir/operand.rs b/src/librustc_trans/mir/operand.rs deleted file mode 100644 index be14da1a195..00000000000 --- a/src/librustc_trans/mir/operand.rs +++ /dev/null @@ -1,427 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::ValueRef; -use rustc::middle::const_val::ConstEvalErr; -use rustc::mir; -use rustc::mir::interpret::ConstValue; -use rustc::ty; -use rustc::ty::layout::{self, Align, LayoutOf, TyLayout}; -use rustc_data_structures::indexed_vec::Idx; - -use base; -use common::{self, CodegenCx, C_null, C_undef, C_usize}; -use builder::{Builder, MemFlags}; -use value::Value; -use type_of::LayoutLlvmExt; -use type_::Type; -use consts; - -use std::fmt; -use std::ptr; - -use super::{FunctionCx, LocalRef}; -use super::constant::{primval_to_llvm, const_alloc_to_llvm}; -use super::place::PlaceRef; - -/// The representation of a Rust value. The enum variant is in fact -/// uniquely determined by the value's type, but is kept as a -/// safety check. -#[derive(Copy, Clone)] -pub enum OperandValue { - /// A reference to the actual operand. The data is guaranteed - /// to be valid for the operand's lifetime. - Ref(ValueRef, Align), - /// A single LLVM value. - Immediate(ValueRef), - /// A pair of immediate LLVM values. Used by fat pointers too. - Pair(ValueRef, ValueRef) -} - -impl fmt::Debug for OperandValue { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - OperandValue::Ref(r, align) => { - write!(f, "Ref({:?}, {:?})", Value(r), align) - } - OperandValue::Immediate(i) => { - write!(f, "Immediate({:?})", Value(i)) - } - OperandValue::Pair(a, b) => { - write!(f, "Pair({:?}, {:?})", Value(a), Value(b)) - } - } - } -} - -/// An `OperandRef` is an "SSA" reference to a Rust value, along with -/// its type. -/// -/// NOTE: unless you know a value's type exactly, you should not -/// generate LLVM opcodes acting on it and instead act via methods, -/// to avoid nasty edge cases. In particular, using `Builder::store` -/// directly is sure to cause problems -- use `OperandRef::store` -/// instead. -#[derive(Copy, Clone)] -pub struct OperandRef<'tcx> { - // The value. - pub val: OperandValue, - - // The layout of value, based on its Rust type. - pub layout: TyLayout<'tcx>, -} - -impl<'tcx> fmt::Debug for OperandRef<'tcx> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "OperandRef({:?} @ {:?})", self.val, self.layout) - } -} - -impl<'a, 'tcx> OperandRef<'tcx> { - pub fn new_zst(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>) -> OperandRef<'tcx> { - assert!(layout.is_zst()); - OperandRef { - val: OperandValue::Immediate(C_undef(layout.immediate_llvm_type(cx))), - layout - } - } - - pub fn from_const(bx: &Builder<'a, 'tcx>, - val: ConstValue<'tcx>, - ty: ty::Ty<'tcx>) - -> Result, ConstEvalErr<'tcx>> { - let layout = bx.cx.layout_of(ty); - - if layout.is_zst() { - return Ok(OperandRef::new_zst(bx.cx, layout)); - } - - let val = match val { - ConstValue::ByVal(x) => { - let scalar = match layout.abi { - layout::Abi::Scalar(ref x) => x, - _ => bug!("from_const: invalid ByVal layout: {:#?}", layout) - }; - let llval = primval_to_llvm( - bx.cx, - x, - scalar, - layout.immediate_llvm_type(bx.cx), - ); - OperandValue::Immediate(llval) - }, - ConstValue::ByValPair(a, b) => { - let (a_scalar, b_scalar) = match layout.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("from_const: invalid ByValPair layout: {:#?}", layout) - }; - let a_llval = primval_to_llvm( - bx.cx, - a, - a_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 0), - ); - let b_llval = primval_to_llvm( - bx.cx, - b, - b_scalar, - layout.scalar_pair_element_llvm_type(bx.cx, 1), - ); - OperandValue::Pair(a_llval, b_llval) - }, - ConstValue::ByRef(alloc) => { - let init = const_alloc_to_llvm(bx.cx, alloc); - let llval = consts::addr_of(bx.cx, init, layout.align, "byte_str"); - let llval = consts::bitcast(llval, layout.llvm_type(bx.cx).ptr_to()); - return Ok(PlaceRef::new_sized(llval, layout, alloc.align).load(bx)); - }, - }; - - Ok(OperandRef { - val, - layout - }) - } - - /// Asserts that this operand refers to a scalar and returns - /// a reference to its value. - pub fn immediate(self) -> ValueRef { - match self.val { - OperandValue::Immediate(s) => s, - _ => bug!("not immediate: {:?}", self) - } - } - - pub fn deref(self, cx: &CodegenCx<'a, 'tcx>) -> PlaceRef<'tcx> { - let projected_ty = self.layout.ty.builtin_deref(true) - .unwrap_or_else(|| bug!("deref of non-pointer {:?}", self)).ty; - let (llptr, llextra) = match self.val { - OperandValue::Immediate(llptr) => (llptr, ptr::null_mut()), - OperandValue::Pair(llptr, llextra) => (llptr, llextra), - OperandValue::Ref(..) => bug!("Deref of by-Ref operand {:?}", self) - }; - let layout = cx.layout_of(projected_ty); - PlaceRef { - llval: llptr, - llextra, - layout, - align: layout.align, - } - } - - /// If this operand is a `Pair`, we return an aggregate with the two values. - /// For other cases, see `immediate`. - pub fn immediate_or_packed_pair(self, bx: &Builder<'a, 'tcx>) -> ValueRef { - if let OperandValue::Pair(a, b) = self.val { - let llty = self.layout.llvm_type(bx.cx); - debug!("Operand::immediate_or_packed_pair: packing {:?} into {:?}", - self, llty); - // Reconstruct the immediate aggregate. - let mut llpair = C_undef(llty); - llpair = bx.insert_value(llpair, a, 0); - llpair = bx.insert_value(llpair, b, 1); - llpair - } else { - self.immediate() - } - } - - /// If the type is a pair, we return a `Pair`, otherwise, an `Immediate`. - pub fn from_immediate_or_packed_pair(bx: &Builder<'a, 'tcx>, - llval: ValueRef, - layout: TyLayout<'tcx>) - -> OperandRef<'tcx> { - let val = if layout.is_llvm_scalar_pair() { - debug!("Operand::from_immediate_or_packed_pair: unpacking {:?} @ {:?}", - llval, layout); - - // Deconstruct the immediate aggregate. - OperandValue::Pair(bx.extract_value(llval, 0), - bx.extract_value(llval, 1)) - } else { - OperandValue::Immediate(llval) - }; - OperandRef { val, layout } - } - - pub fn extract_field(&self, bx: &Builder<'a, 'tcx>, i: usize) -> OperandRef<'tcx> { - let field = self.layout.field(bx.cx, i); - let offset = self.layout.fields.offset(i); - - let mut val = match (self.val, &self.layout.abi) { - // If the field is ZST, it has no data. - _ if field.is_zst() => { - return OperandRef::new_zst(bx.cx, field); - } - - // Newtype of a scalar, scalar pair or vector. - (OperandValue::Immediate(_), _) | - (OperandValue::Pair(..), _) if field.size == self.layout.size => { - assert_eq!(offset.bytes(), 0); - self.val - } - - // Extract a scalar component from a pair. - (OperandValue::Pair(a_llval, b_llval), &layout::Abi::ScalarPair(ref a, ref b)) => { - if offset.bytes() == 0 { - assert_eq!(field.size, a.value.size(bx.cx)); - OperandValue::Immediate(a_llval) - } else { - assert_eq!(offset, a.value.size(bx.cx) - .abi_align(b.value.align(bx.cx))); - assert_eq!(field.size, b.value.size(bx.cx)); - OperandValue::Immediate(b_llval) - } - } - - // `#[repr(simd)]` types are also immediate. - (OperandValue::Immediate(llval), &layout::Abi::Vector { .. }) => { - OperandValue::Immediate( - bx.extract_element(llval, C_usize(bx.cx, i as u64))) - } - - _ => bug!("OperandRef::extract_field({:?}): not applicable", self) - }; - - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - match val { - OperandValue::Immediate(ref mut llval) => { - *llval = bx.bitcast(*llval, field.immediate_llvm_type(bx.cx)); - } - OperandValue::Pair(ref mut a, ref mut b) => { - *a = bx.bitcast(*a, field.scalar_pair_element_llvm_type(bx.cx, 0)); - *b = bx.bitcast(*b, field.scalar_pair_element_llvm_type(bx.cx, 1)); - } - OperandValue::Ref(..) => bug!() - } - - OperandRef { - val, - layout: field - } - } -} - -impl<'a, 'tcx> OperandValue { - pub fn store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::empty()); - } - - pub fn volatile_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::VOLATILE); - } - - pub fn nontemporal_store(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>) { - self.store_with_flags(bx, dest, MemFlags::NONTEMPORAL); - } - - fn store_with_flags(self, bx: &Builder<'a, 'tcx>, dest: PlaceRef<'tcx>, flags: MemFlags) { - debug!("OperandRef::store: operand={:?}, dest={:?}", self, dest); - // Avoid generating stores of zero-sized values, because the only way to have a zero-sized - // value is through `undef`, and store itself is useless. - if dest.layout.is_zst() { - return; - } - match self { - OperandValue::Ref(r, source_align) => { - base::memcpy_ty(bx, dest.llval, r, dest.layout, - source_align.min(dest.align), flags) - } - OperandValue::Immediate(s) => { - let val = base::from_immediate(bx, s); - bx.store_with_flags(val, dest.llval, dest.align, flags); - } - OperandValue::Pair(a, b) => { - for (i, &x) in [a, b].iter().enumerate() { - let mut llptr = bx.struct_gep(dest.llval, i as u64); - // Make sure to always store i1 as i8. - if common::val_ty(x) == Type::i1(bx.cx) { - llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); - } - let val = base::from_immediate(bx, x); - bx.store_with_flags(val, llptr, dest.align, flags); - } - } - } - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - fn maybe_trans_consume_direct(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> Option> - { - debug!("maybe_trans_consume_direct(place={:?})", place); - - // watch out for locals that do not have an - // alloca; they are handled somewhat differently - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Operand(Some(o)) => { - return Some(o); - } - LocalRef::Operand(None) => { - bug!("use of {:?} before def", place); - } - LocalRef::Place(..) => { - // use path below - } - } - } - - // Moves out of scalar and scalar pair fields are trivial. - if let &mir::Place::Projection(ref proj) = place { - if let Some(o) = self.maybe_trans_consume_direct(bx, &proj.base) { - match proj.elem { - mir::ProjectionElem::Field(ref f, _) => { - return Some(o.extract_field(bx, f.index())); - } - mir::ProjectionElem::Index(_) | - mir::ProjectionElem::ConstantIndex { .. } => { - // ZSTs don't require any actual memory access. - // FIXME(eddyb) deduplicate this with the identical - // checks in `trans_consume` and `extract_field`. - let elem = o.layout.field(bx.cx, 0); - if elem.is_zst() { - return Some(OperandRef::new_zst(bx.cx, elem)); - } - } - _ => {} - } - } - } - - None - } - - pub fn trans_consume(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> OperandRef<'tcx> - { - debug!("trans_consume(place={:?})", place); - - let ty = self.monomorphized_place_ty(place); - let layout = bx.cx.layout_of(ty); - - // ZSTs don't require any actual memory access. - if layout.is_zst() { - return OperandRef::new_zst(bx.cx, layout); - } - - if let Some(o) = self.maybe_trans_consume_direct(bx, place) { - return o; - } - - // for most places, to consume them we just load them - // out from their home - self.trans_place(bx, place).load(bx) - } - - pub fn trans_operand(&mut self, - bx: &Builder<'a, 'tcx>, - operand: &mir::Operand<'tcx>) - -> OperandRef<'tcx> - { - debug!("trans_operand(operand={:?})", operand); - - match *operand { - mir::Operand::Copy(ref place) | - mir::Operand::Move(ref place) => { - self.trans_consume(bx, place) - } - - mir::Operand::Constant(ref constant) => { - let ty = self.monomorphize(&constant.ty); - self.mir_constant_to_const_value(bx, constant) - .and_then(|c| OperandRef::from_const(bx, c, ty)) - .unwrap_or_else(|err| { - match constant.literal { - mir::Literal::Promoted { .. } => { - // don't report errors inside promoteds, just warnings. - }, - mir::Literal::Value { .. } => { - err.report(bx.tcx(), constant.span, "const operand") - }, - } - // We've errored, so we don't have to produce working code. - let layout = bx.cx.layout_of(ty); - PlaceRef::new_sized( - C_null(layout.llvm_type(bx.cx).ptr_to()), - layout, - layout.align, - ).load(bx) - }) - } - } - } -} diff --git a/src/librustc_trans/mir/place.rs b/src/librustc_trans/mir/place.rs deleted file mode 100644 index d4abd5fa88d..00000000000 --- a/src/librustc_trans/mir/place.rs +++ /dev/null @@ -1,499 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::ty::{self, Ty}; -use rustc::ty::layout::{self, Align, TyLayout, LayoutOf}; -use rustc::mir; -use rustc::mir::tcx::PlaceTy; -use rustc_data_structures::indexed_vec::Idx; -use base; -use builder::Builder; -use common::{CodegenCx, C_undef, C_usize, C_u8, C_u32, C_uint, C_null, C_uint_big}; -use consts; -use type_of::LayoutLlvmExt; -use type_::Type; -use value::Value; -use glue; - -use std::ptr; - -use super::{FunctionCx, LocalRef}; -use super::operand::{OperandRef, OperandValue}; - -#[derive(Copy, Clone, Debug)] -pub struct PlaceRef<'tcx> { - /// Pointer to the contents of the place - pub llval: ValueRef, - - /// This place's extra data if it is unsized, or null - pub llextra: ValueRef, - - /// Monomorphized type of this place, including variant information - pub layout: TyLayout<'tcx>, - - /// What alignment we know for this place - pub align: Align, -} - -impl<'a, 'tcx> PlaceRef<'tcx> { - pub fn new_sized(llval: ValueRef, - layout: TyLayout<'tcx>, - align: Align) - -> PlaceRef<'tcx> { - PlaceRef { - llval, - llextra: ptr::null_mut(), - layout, - align - } - } - - pub fn alloca(bx: &Builder<'a, 'tcx>, layout: TyLayout<'tcx>, name: &str) - -> PlaceRef<'tcx> { - debug!("alloca({:?}: {:?})", name, layout); - let tmp = bx.alloca(layout.llvm_type(bx.cx), name, layout.align); - Self::new_sized(tmp, layout, layout.align) - } - - pub fn len(&self, cx: &CodegenCx<'a, 'tcx>) -> ValueRef { - if let layout::FieldPlacement::Array { count, .. } = self.layout.fields { - if self.layout.is_unsized() { - assert!(self.has_extra()); - assert_eq!(count, 0); - self.llextra - } else { - C_usize(cx, count) - } - } else { - bug!("unexpected layout `{:#?}` in PlaceRef::len", self.layout) - } - } - - pub fn has_extra(&self) -> bool { - !self.llextra.is_null() - } - - pub fn load(&self, bx: &Builder<'a, 'tcx>) -> OperandRef<'tcx> { - debug!("PlaceRef::load: {:?}", self); - - assert!(!self.has_extra()); - - if self.layout.is_zst() { - return OperandRef::new_zst(bx.cx, self.layout); - } - - let scalar_load_metadata = |load, scalar: &layout::Scalar| { - let vr = scalar.valid_range.clone(); - match scalar.value { - layout::Int(..) => { - let range = scalar.valid_range_exclusive(bx.cx); - if range.start != range.end { - bx.range_metadata(load, range); - } - } - layout::Pointer if vr.start() < vr.end() && !vr.contains(&0) => { - bx.nonnull_metadata(load); - } - _ => {} - } - }; - - let val = if self.layout.is_llvm_immediate() { - let mut const_llval = ptr::null_mut(); - unsafe { - let global = llvm::LLVMIsAGlobalVariable(self.llval); - if !global.is_null() && llvm::LLVMIsGlobalConstant(global) == llvm::True { - const_llval = llvm::LLVMGetInitializer(global); - } - } - - let llval = if !const_llval.is_null() { - const_llval - } else { - let load = bx.load(self.llval, self.align); - if let layout::Abi::Scalar(ref scalar) = self.layout.abi { - scalar_load_metadata(load, scalar); - } - load - }; - OperandValue::Immediate(base::to_immediate(bx, llval, self.layout)) - } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { - let load = |i, scalar: &layout::Scalar| { - let mut llptr = bx.struct_gep(self.llval, i as u64); - // Make sure to always load i1 as i8. - if scalar.is_bool() { - llptr = bx.pointercast(llptr, Type::i8p(bx.cx)); - } - let load = bx.load(llptr, self.align); - scalar_load_metadata(load, scalar); - if scalar.is_bool() { - bx.trunc(load, Type::i1(bx.cx)) - } else { - load - } - }; - OperandValue::Pair(load(0, a), load(1, b)) - } else { - OperandValue::Ref(self.llval, self.align) - }; - - OperandRef { val, layout: self.layout } - } - - /// Access a field, at a point when the value's case is known. - pub fn project_field(self, bx: &Builder<'a, 'tcx>, ix: usize) -> PlaceRef<'tcx> { - let cx = bx.cx; - let field = self.layout.field(cx, ix); - let offset = self.layout.fields.offset(ix); - let align = self.align.min(self.layout.align).min(field.align); - - let simple = || { - // Unions and newtypes only use an offset of 0. - let llval = if offset.bytes() == 0 { - self.llval - } else if let layout::Abi::ScalarPair(ref a, ref b) = self.layout.abi { - // Offsets have to match either first or second field. - assert_eq!(offset, a.value.size(cx).abi_align(b.value.align(cx))); - bx.struct_gep(self.llval, 1) - } else { - bx.struct_gep(self.llval, self.layout.llvm_field_index(ix)) - }; - PlaceRef { - // HACK(eddyb) have to bitcast pointers until LLVM removes pointee types. - llval: bx.pointercast(llval, field.llvm_type(cx).ptr_to()), - llextra: if cx.type_has_metadata(field.ty) { - self.llextra - } else { - ptr::null_mut() - }, - layout: field, - align, - } - }; - - // Simple cases, which don't need DST adjustment: - // * no metadata available - just log the case - // * known alignment - sized types, [T], str or a foreign type - // * packed struct - there is no alignment padding - match field.ty.sty { - _ if !self.has_extra() => { - debug!("Unsized field `{}`, of `{:?}` has no metadata for adjustment", - ix, Value(self.llval)); - return simple(); - } - _ if !field.is_unsized() => return simple(), - ty::TySlice(..) | ty::TyStr | ty::TyForeign(..) => return simple(), - ty::TyAdt(def, _) => { - if def.repr.packed() { - // FIXME(eddyb) generalize the adjustment when we - // start supporting packing to larger alignments. - assert_eq!(self.layout.align.abi(), 1); - return simple(); - } - } - _ => {} - } - - // We need to get the pointer manually now. - // We do this by casting to a *i8, then offsetting it by the appropriate amount. - // We do this instead of, say, simply adjusting the pointer from the result of a GEP - // because the field may have an arbitrary alignment in the LLVM representation - // anyway. - // - // To demonstrate: - // struct Foo { - // x: u16, - // y: T - // } - // - // The type Foo> is represented in LLVM as { u16, { u16, u8 }}, meaning that - // the `y` field has 16-bit alignment. - - let meta = self.llextra; - - let unaligned_offset = C_usize(cx, offset.bytes()); - - // Get the alignment of the field - let (_, unsized_align) = glue::size_and_align_of_dst(bx, field.ty, meta); - - // Bump the unaligned offset up to the appropriate alignment using the - // following expression: - // - // (unaligned offset + (align - 1)) & -align - - // Calculate offset - let align_sub_1 = bx.sub(unsized_align, C_usize(cx, 1u64)); - let offset = bx.and(bx.add(unaligned_offset, align_sub_1), - bx.neg(unsized_align)); - - debug!("struct_field_ptr: DST field offset: {:?}", Value(offset)); - - // Cast and adjust pointer - let byte_ptr = bx.pointercast(self.llval, Type::i8p(cx)); - let byte_ptr = bx.gep(byte_ptr, &[offset]); - - // Finally, cast back to the type expected - let ll_fty = field.llvm_type(cx); - debug!("struct_field_ptr: Field type is {:?}", ll_fty); - - PlaceRef { - llval: bx.pointercast(byte_ptr, ll_fty.ptr_to()), - llextra: self.llextra, - layout: field, - align, - } - } - - /// Obtain the actual discriminant of a value. - pub fn trans_get_discr(self, bx: &Builder<'a, 'tcx>, cast_to: Ty<'tcx>) -> ValueRef { - let cast_to = bx.cx.layout_of(cast_to).immediate_llvm_type(bx.cx); - if self.layout.abi == layout::Abi::Uninhabited { - return C_undef(cast_to); - } - match self.layout.variants { - layout::Variants::Single { index } => { - let discr_val = self.layout.ty.ty_adt_def().map_or( - index as u128, - |def| def.discriminant_for_variant(bx.cx.tcx, index).val); - return C_uint_big(cast_to, discr_val); - } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } - - let discr = self.project_field(bx, 0); - let lldiscr = discr.load(bx).immediate(); - match self.layout.variants { - layout::Variants::Single { .. } => bug!(), - layout::Variants::Tagged { ref tag, .. } => { - let signed = match tag.value { - layout::Int(_, signed) => signed, - _ => false - }; - bx.intcast(lldiscr, cast_to, signed) - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - let niche_llty = discr.layout.immediate_llvm_type(bx.cx); - if niche_variants.start() == niche_variants.end() { - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_start == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_start) - }; - bx.select(bx.icmp(llvm::IntEQ, lldiscr, niche_llval), - C_uint(cast_to, *niche_variants.start() as u64), - C_uint(cast_to, dataful_variant as u64)) - } else { - // Rebase from niche values to discriminant values. - let delta = niche_start.wrapping_sub(*niche_variants.start() as u128); - let lldiscr = bx.sub(lldiscr, C_uint_big(niche_llty, delta)); - let lldiscr_max = C_uint(niche_llty, *niche_variants.end() as u64); - bx.select(bx.icmp(llvm::IntULE, lldiscr, lldiscr_max), - bx.intcast(lldiscr, cast_to, false), - C_uint(cast_to, dataful_variant as u64)) - } - } - } - } - - /// Set the discriminant for a new value of the given case of the given - /// representation. - pub fn trans_set_discr(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) { - if self.layout.for_variant(bx.cx, variant_index).abi == layout::Abi::Uninhabited { - return; - } - match self.layout.variants { - layout::Variants::Single { index } => { - assert_eq!(index, variant_index); - } - layout::Variants::Tagged { .. } => { - let ptr = self.project_field(bx, 0); - let to = self.layout.ty.ty_adt_def().unwrap() - .discriminant_for_variant(bx.tcx(), variant_index) - .val; - bx.store( - C_uint_big(ptr.layout.llvm_type(bx.cx), to), - ptr.llval, - ptr.align); - } - layout::Variants::NicheFilling { - dataful_variant, - ref niche_variants, - niche_start, - .. - } => { - if variant_index != dataful_variant { - if bx.sess().target.target.arch == "arm" || - bx.sess().target.target.arch == "aarch64" { - // Issue #34427: As workaround for LLVM bug on ARM, - // use memset of 0 before assigning niche value. - let llptr = bx.pointercast(self.llval, Type::i8(bx.cx).ptr_to()); - let fill_byte = C_u8(bx.cx, 0); - let (size, align) = self.layout.size_and_align(); - let size = C_usize(bx.cx, size.bytes()); - let align = C_u32(bx.cx, align.abi() as u32); - base::call_memset(bx, llptr, fill_byte, size, align, false); - } - - let niche = self.project_field(bx, 0); - let niche_llty = niche.layout.immediate_llvm_type(bx.cx); - let niche_value = ((variant_index - *niche_variants.start()) as u128) - .wrapping_add(niche_start); - // FIXME(eddyb) Check the actual primitive type here. - let niche_llval = if niche_value == 0 { - // HACK(eddyb) Using `C_null` as it works on all types. - C_null(niche_llty) - } else { - C_uint_big(niche_llty, niche_value) - }; - OperandValue::Immediate(niche_llval).store(bx, niche); - } - } - } - } - - pub fn project_index(&self, bx: &Builder<'a, 'tcx>, llindex: ValueRef) - -> PlaceRef<'tcx> { - PlaceRef { - llval: bx.inbounds_gep(self.llval, &[C_usize(bx.cx, 0), llindex]), - llextra: ptr::null_mut(), - layout: self.layout.field(bx.cx, 0), - align: self.align - } - } - - pub fn project_downcast(&self, bx: &Builder<'a, 'tcx>, variant_index: usize) - -> PlaceRef<'tcx> { - let mut downcast = *self; - downcast.layout = self.layout.for_variant(bx.cx, variant_index); - - // Cast to the appropriate variant struct type. - let variant_ty = downcast.layout.llvm_type(bx.cx); - downcast.llval = bx.pointercast(downcast.llval, variant_ty.ptr_to()); - - downcast - } - - pub fn storage_live(&self, bx: &Builder<'a, 'tcx>) { - bx.lifetime_start(self.llval, self.layout.size); - } - - pub fn storage_dead(&self, bx: &Builder<'a, 'tcx>) { - bx.lifetime_end(self.llval, self.layout.size); - } -} - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_place(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) - -> PlaceRef<'tcx> { - debug!("trans_place(place={:?})", place); - - let cx = bx.cx; - let tcx = cx.tcx; - - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(place) => { - return place; - } - LocalRef::Operand(..) => { - bug!("using operand local {:?} as place", place); - } - } - } - - let result = match *place { - mir::Place::Local(_) => bug!(), // handled above - mir::Place::Static(box mir::Static { def_id, ty }) => { - let layout = cx.layout_of(self.monomorphize(&ty)); - PlaceRef::new_sized(consts::get_static(cx, def_id), layout, layout.align) - }, - mir::Place::Projection(box mir::Projection { - ref base, - elem: mir::ProjectionElem::Deref - }) => { - // Load the pointer from its location. - self.trans_consume(bx, base).deref(bx.cx) - } - mir::Place::Projection(ref projection) => { - let tr_base = self.trans_place(bx, &projection.base); - - match projection.elem { - mir::ProjectionElem::Deref => bug!(), - mir::ProjectionElem::Field(ref field, _) => { - tr_base.project_field(bx, field.index()) - } - mir::ProjectionElem::Index(index) => { - let index = &mir::Operand::Copy(mir::Place::Local(index)); - let index = self.trans_operand(bx, index); - let llindex = index.immediate(); - tr_base.project_index(bx, llindex) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: false, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - tr_base.project_index(bx, lloffset) - } - mir::ProjectionElem::ConstantIndex { offset, - from_end: true, - min_length: _ } => { - let lloffset = C_usize(bx.cx, offset as u64); - let lllen = tr_base.len(bx.cx); - let llindex = bx.sub(lllen, lloffset); - tr_base.project_index(bx, llindex) - } - mir::ProjectionElem::Subslice { from, to } => { - let mut subslice = tr_base.project_index(bx, - C_usize(bx.cx, from as u64)); - let projected_ty = PlaceTy::Ty { ty: tr_base.layout.ty } - .projection_ty(tcx, &projection.elem).to_ty(bx.tcx()); - subslice.layout = bx.cx.layout_of(self.monomorphize(&projected_ty)); - - if subslice.layout.is_unsized() { - assert!(tr_base.has_extra()); - subslice.llextra = bx.sub(tr_base.llextra, - C_usize(bx.cx, (from as u64) + (to as u64))); - } - - // Cast the place pointer type to the new - // array or slice type (*[%_; new_len]). - subslice.llval = bx.pointercast(subslice.llval, - subslice.layout.llvm_type(bx.cx).ptr_to()); - - subslice - } - mir::ProjectionElem::Downcast(_, v) => { - tr_base.project_downcast(bx, v) - } - } - } - }; - debug!("trans_place(place={:?}) => {:?}", place, result); - result - } - - pub fn monomorphized_place_ty(&self, place: &mir::Place<'tcx>) -> Ty<'tcx> { - let tcx = self.cx.tcx; - let place_ty = place.ty(self.mir, tcx); - self.monomorphize(&place_ty.to_ty(tcx)) - } -} - diff --git a/src/librustc_trans/mir/rvalue.rs b/src/librustc_trans/mir/rvalue.rs deleted file mode 100644 index 3b447756450..00000000000 --- a/src/librustc_trans/mir/rvalue.rs +++ /dev/null @@ -1,952 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm::{self, ValueRef}; -use rustc::ty::{self, Ty}; -use rustc::ty::cast::{CastTy, IntTy}; -use rustc::ty::layout::{self, LayoutOf}; -use rustc::mir; -use rustc::middle::lang_items::ExchangeMallocFnLangItem; -use rustc_apfloat::{ieee, Float, Status, Round}; -use std::{u128, i128}; - -use base; -use builder::Builder; -use callee; -use common::{self, val_ty}; -use common::{C_bool, C_u8, C_i32, C_u32, C_u64, C_undef, C_null, C_usize, C_uint, C_uint_big}; -use consts; -use monomorphize; -use type_::Type; -use type_of::LayoutLlvmExt; -use value::Value; - -use super::{FunctionCx, LocalRef}; -use super::operand::{OperandRef, OperandValue}; -use super::place::PlaceRef; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_rvalue(&mut self, - bx: Builder<'a, 'tcx>, - dest: PlaceRef<'tcx>, - rvalue: &mir::Rvalue<'tcx>) - -> Builder<'a, 'tcx> - { - debug!("trans_rvalue(dest.llval={:?}, rvalue={:?})", - Value(dest.llval), rvalue); - - match *rvalue { - mir::Rvalue::Use(ref operand) => { - let tr_operand = self.trans_operand(&bx, operand); - // FIXME: consider not copying constants through stack. (fixable by translating - // constants into OperandValue::Ref, why don’t we do that yet if we don’t?) - tr_operand.val.store(&bx, dest); - bx - } - - mir::Rvalue::Cast(mir::CastKind::Unsize, ref source, _) => { - // The destination necessarily contains a fat pointer, so if - // it's a scalar pair, it's a fat pointer or newtype thereof. - if dest.layout.is_llvm_scalar_pair() { - // into-coerce of a thin pointer to a fat pointer - just - // use the operand path. - let (bx, temp) = self.trans_rvalue_operand(bx, rvalue); - temp.val.store(&bx, dest); - return bx; - } - - // Unsize of a nontrivial struct. I would prefer for - // this to be eliminated by MIR translation, but - // `CoerceUnsized` can be passed by a where-clause, - // so the (generic) MIR may not be able to expand it. - let operand = self.trans_operand(&bx, source); - match operand.val { - OperandValue::Pair(..) | - OperandValue::Immediate(_) => { - // unsize from an immediate structure. We don't - // really need a temporary alloca here, but - // avoiding it would require us to have - // `coerce_unsized_into` use extractvalue to - // index into the struct, and this case isn't - // important enough for it. - debug!("trans_rvalue: creating ugly alloca"); - let scratch = PlaceRef::alloca(&bx, operand.layout, "__unsize_temp"); - scratch.storage_live(&bx); - operand.val.store(&bx, scratch); - base::coerce_unsized_into(&bx, scratch, dest); - scratch.storage_dead(&bx); - } - OperandValue::Ref(llref, align) => { - let source = PlaceRef::new_sized(llref, operand.layout, align); - base::coerce_unsized_into(&bx, source, dest); - } - } - bx - } - - mir::Rvalue::Repeat(ref elem, count) => { - let tr_elem = self.trans_operand(&bx, elem); - - // Do not generate the loop for zero-sized elements or empty arrays. - if dest.layout.is_zst() { - return bx; - } - - let start = dest.project_index(&bx, C_usize(bx.cx, 0)).llval; - - if let OperandValue::Immediate(v) = tr_elem.val { - let align = C_i32(bx.cx, dest.align.abi() as i32); - let size = C_usize(bx.cx, dest.layout.size.bytes()); - - // Use llvm.memset.p0i8.* to initialize all zero arrays - if common::is_const_integral(v) && common::const_to_uint(v) == 0 { - let fill = C_u8(bx.cx, 0); - base::call_memset(&bx, start, fill, size, align, false); - return bx; - } - - // Use llvm.memset.p0i8.* to initialize byte arrays - let v = base::from_immediate(&bx, v); - if common::val_ty(v) == Type::i8(bx.cx) { - base::call_memset(&bx, start, v, size, align, false); - return bx; - } - } - - let count = C_usize(bx.cx, count); - let end = dest.project_index(&bx, count).llval; - - let header_bx = bx.build_sibling_block("repeat_loop_header"); - let body_bx = bx.build_sibling_block("repeat_loop_body"); - let next_bx = bx.build_sibling_block("repeat_loop_next"); - - bx.br(header_bx.llbb()); - let current = header_bx.phi(common::val_ty(start), &[start], &[bx.llbb()]); - - let keep_going = header_bx.icmp(llvm::IntNE, current, end); - header_bx.cond_br(keep_going, body_bx.llbb(), next_bx.llbb()); - - tr_elem.val.store(&body_bx, - PlaceRef::new_sized(current, tr_elem.layout, dest.align)); - - let next = body_bx.inbounds_gep(current, &[C_usize(bx.cx, 1)]); - body_bx.br(header_bx.llbb()); - header_bx.add_incoming_to_phi(current, next, body_bx.llbb()); - - next_bx - } - - mir::Rvalue::Aggregate(ref kind, ref operands) => { - let (dest, active_field_index) = match **kind { - mir::AggregateKind::Adt(adt_def, variant_index, _, active_field_index) => { - dest.trans_set_discr(&bx, variant_index); - if adt_def.is_enum() { - (dest.project_downcast(&bx, variant_index), active_field_index) - } else { - (dest, active_field_index) - } - } - _ => (dest, None) - }; - for (i, operand) in operands.iter().enumerate() { - let op = self.trans_operand(&bx, operand); - // Do not generate stores and GEPis for zero-sized fields. - if !op.layout.is_zst() { - let field_index = active_field_index.unwrap_or(i); - op.val.store(&bx, dest.project_field(&bx, field_index)); - } - } - bx - } - - _ => { - assert!(self.rvalue_creates_operand(rvalue)); - let (bx, temp) = self.trans_rvalue_operand(bx, rvalue); - temp.val.store(&bx, dest); - bx - } - } - } - - pub fn trans_rvalue_operand(&mut self, - bx: Builder<'a, 'tcx>, - rvalue: &mir::Rvalue<'tcx>) - -> (Builder<'a, 'tcx>, OperandRef<'tcx>) - { - assert!(self.rvalue_creates_operand(rvalue), "cannot trans {:?} to operand", rvalue); - - match *rvalue { - mir::Rvalue::Cast(ref kind, ref source, mir_cast_ty) => { - let operand = self.trans_operand(&bx, source); - debug!("cast operand is {:?}", operand); - let cast = bx.cx.layout_of(self.monomorphize(&mir_cast_ty)); - - let val = match *kind { - mir::CastKind::ReifyFnPointer => { - match operand.layout.ty.sty { - ty::TyFnDef(def_id, substs) => { - if bx.cx.tcx.has_attr(def_id, "rustc_args_required_const") { - bug!("reifying a fn ptr that requires \ - const arguments"); - } - OperandValue::Immediate( - callee::resolve_and_get_fn(bx.cx, def_id, substs)) - } - _ => { - bug!("{} cannot be reified to a fn ptr", operand.layout.ty) - } - } - } - mir::CastKind::ClosureFnPointer => { - match operand.layout.ty.sty { - ty::TyClosure(def_id, substs) => { - let instance = monomorphize::resolve_closure( - bx.cx.tcx, def_id, substs, ty::ClosureKind::FnOnce); - OperandValue::Immediate(callee::get_fn(bx.cx, instance)) - } - _ => { - bug!("{} cannot be cast to a fn ptr", operand.layout.ty) - } - } - } - mir::CastKind::UnsafeFnPointer => { - // this is a no-op at the LLVM level - operand.val - } - mir::CastKind::Unsize => { - assert!(cast.is_llvm_scalar_pair()); - match operand.val { - OperandValue::Pair(lldata, llextra) => { - // unsize from a fat pointer - this is a - // "trait-object-to-supertrait" coercion, for - // example, - // &'a fmt::Debug+Send => &'a fmt::Debug, - - // HACK(eddyb) have to bitcast pointers - // until LLVM removes pointee types. - let lldata = bx.pointercast(lldata, - cast.scalar_pair_element_llvm_type(bx.cx, 0)); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Immediate(lldata) => { - // "standard" unsize - let (lldata, llextra) = base::unsize_thin_ptr(&bx, lldata, - operand.layout.ty, cast.ty); - OperandValue::Pair(lldata, llextra) - } - OperandValue::Ref(..) => { - bug!("by-ref operand {:?} in trans_rvalue_operand", - operand); - } - } - } - mir::CastKind::Misc if operand.layout.is_llvm_scalar_pair() => { - if let OperandValue::Pair(data_ptr, meta) = operand.val { - if cast.is_llvm_scalar_pair() { - let data_cast = bx.pointercast(data_ptr, - cast.scalar_pair_element_llvm_type(bx.cx, 0)); - OperandValue::Pair(data_cast, meta) - } else { // cast to thin-ptr - // Cast of fat-ptr to thin-ptr is an extraction of data-ptr and - // pointer-cast of that pointer to desired pointer type. - let llcast_ty = cast.immediate_llvm_type(bx.cx); - let llval = bx.pointercast(data_ptr, llcast_ty); - OperandValue::Immediate(llval) - } - } else { - bug!("Unexpected non-Pair operand") - } - } - mir::CastKind::Misc => { - assert!(cast.is_llvm_immediate()); - let ll_t_out = cast.immediate_llvm_type(bx.cx); - if operand.layout.abi == layout::Abi::Uninhabited { - return (bx, OperandRef { - val: OperandValue::Immediate(C_undef(ll_t_out)), - layout: cast, - }); - } - let r_t_in = CastTy::from_ty(operand.layout.ty) - .expect("bad input type for cast"); - let r_t_out = CastTy::from_ty(cast.ty).expect("bad output type for cast"); - let ll_t_in = operand.layout.immediate_llvm_type(bx.cx); - match operand.layout.variants { - layout::Variants::Single { index } => { - if let Some(def) = operand.layout.ty.ty_adt_def() { - let discr_val = def - .discriminant_for_variant(bx.cx.tcx, index) - .val; - let discr = C_uint_big(ll_t_out, discr_val); - return (bx, OperandRef { - val: OperandValue::Immediate(discr), - layout: cast, - }); - } - } - layout::Variants::Tagged { .. } | - layout::Variants::NicheFilling { .. } => {}, - } - let llval = operand.immediate(); - - let mut signed = false; - if let layout::Abi::Scalar(ref scalar) = operand.layout.abi { - if let layout::Int(_, s) = scalar.value { - signed = s; - - if scalar.valid_range.end() > scalar.valid_range.start() { - // We want `table[e as usize]` to not - // have bound checks, and this is the most - // convenient place to put the `assume`. - - base::call_assume(&bx, bx.icmp( - llvm::IntULE, - llval, - C_uint_big(ll_t_in, *scalar.valid_range.end()) - )); - } - } - } - - let newval = match (r_t_in, r_t_out) { - (CastTy::Int(_), CastTy::Int(_)) => { - bx.intcast(llval, ll_t_out, signed) - } - (CastTy::Float, CastTy::Float) => { - let srcsz = ll_t_in.float_width(); - let dstsz = ll_t_out.float_width(); - if dstsz > srcsz { - bx.fpext(llval, ll_t_out) - } else if srcsz > dstsz { - bx.fptrunc(llval, ll_t_out) - } else { - llval - } - } - (CastTy::Ptr(_), CastTy::Ptr(_)) | - (CastTy::FnPtr, CastTy::Ptr(_)) | - (CastTy::RPtr(_), CastTy::Ptr(_)) => - bx.pointercast(llval, ll_t_out), - (CastTy::Ptr(_), CastTy::Int(_)) | - (CastTy::FnPtr, CastTy::Int(_)) => - bx.ptrtoint(llval, ll_t_out), - (CastTy::Int(_), CastTy::Ptr(_)) => { - let usize_llval = bx.intcast(llval, bx.cx.isize_ty, signed); - bx.inttoptr(usize_llval, ll_t_out) - } - (CastTy::Int(_), CastTy::Float) => - cast_int_to_float(&bx, signed, llval, ll_t_in, ll_t_out), - (CastTy::Float, CastTy::Int(IntTy::I)) => - cast_float_to_int(&bx, true, llval, ll_t_in, ll_t_out), - (CastTy::Float, CastTy::Int(_)) => - cast_float_to_int(&bx, false, llval, ll_t_in, ll_t_out), - _ => bug!("unsupported cast: {:?} to {:?}", operand.layout.ty, cast.ty) - }; - OperandValue::Immediate(newval) - } - }; - (bx, OperandRef { - val, - layout: cast - }) - } - - mir::Rvalue::Ref(_, bk, ref place) => { - let tr_place = self.trans_place(&bx, place); - - let ty = tr_place.layout.ty; - - // Note: places are indirect, so storing the `llval` into the - // destination effectively creates a reference. - let val = if !bx.cx.type_has_metadata(ty) { - OperandValue::Immediate(tr_place.llval) - } else { - OperandValue::Pair(tr_place.llval, tr_place.llextra) - }; - (bx, OperandRef { - val, - layout: self.cx.layout_of(self.cx.tcx.mk_ref( - self.cx.tcx.types.re_erased, - ty::TypeAndMut { ty, mutbl: bk.to_mutbl_lossy() } - )), - }) - } - - mir::Rvalue::Len(ref place) => { - let size = self.evaluate_array_len(&bx, place); - let operand = OperandRef { - val: OperandValue::Immediate(size), - layout: bx.cx.layout_of(bx.tcx().types.usize), - }; - (bx, operand) - } - - mir::Rvalue::BinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.trans_operand(&bx, lhs); - let rhs = self.trans_operand(&bx, rhs); - let llresult = match (lhs.val, rhs.val) { - (OperandValue::Pair(lhs_addr, lhs_extra), - OperandValue::Pair(rhs_addr, rhs_extra)) => { - self.trans_fat_ptr_binop(&bx, op, - lhs_addr, lhs_extra, - rhs_addr, rhs_extra, - lhs.layout.ty) - } - - (OperandValue::Immediate(lhs_val), - OperandValue::Immediate(rhs_val)) => { - self.trans_scalar_binop(&bx, op, lhs_val, rhs_val, lhs.layout.ty) - } - - _ => bug!() - }; - let operand = OperandRef { - val: OperandValue::Immediate(llresult), - layout: bx.cx.layout_of( - op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty)), - }; - (bx, operand) - } - mir::Rvalue::CheckedBinaryOp(op, ref lhs, ref rhs) => { - let lhs = self.trans_operand(&bx, lhs); - let rhs = self.trans_operand(&bx, rhs); - let result = self.trans_scalar_checked_binop(&bx, op, - lhs.immediate(), rhs.immediate(), - lhs.layout.ty); - let val_ty = op.ty(bx.tcx(), lhs.layout.ty, rhs.layout.ty); - let operand_ty = bx.tcx().intern_tup(&[val_ty, bx.tcx().types.bool]); - let operand = OperandRef { - val: result, - layout: bx.cx.layout_of(operand_ty) - }; - - (bx, operand) - } - - mir::Rvalue::UnaryOp(op, ref operand) => { - let operand = self.trans_operand(&bx, operand); - let lloperand = operand.immediate(); - let is_float = operand.layout.ty.is_fp(); - let llval = match op { - mir::UnOp::Not => bx.not(lloperand), - mir::UnOp::Neg => if is_float { - bx.fneg(lloperand) - } else { - bx.neg(lloperand) - } - }; - (bx, OperandRef { - val: OperandValue::Immediate(llval), - layout: operand.layout, - }) - } - - mir::Rvalue::Discriminant(ref place) => { - let discr_ty = rvalue.ty(&*self.mir, bx.tcx()); - let discr = self.trans_place(&bx, place) - .trans_get_discr(&bx, discr_ty); - (bx, OperandRef { - val: OperandValue::Immediate(discr), - layout: self.cx.layout_of(discr_ty) - }) - } - - mir::Rvalue::NullaryOp(mir::NullOp::SizeOf, ty) => { - assert!(bx.cx.type_is_sized(ty)); - let val = C_usize(bx.cx, bx.cx.size_of(ty).bytes()); - let tcx = bx.tcx(); - (bx, OperandRef { - val: OperandValue::Immediate(val), - layout: self.cx.layout_of(tcx.types.usize), - }) - } - - mir::Rvalue::NullaryOp(mir::NullOp::Box, content_ty) => { - let content_ty: Ty<'tcx> = self.monomorphize(&content_ty); - let (size, align) = bx.cx.size_and_align_of(content_ty); - let llsize = C_usize(bx.cx, size.bytes()); - let llalign = C_usize(bx.cx, align.abi()); - let box_layout = bx.cx.layout_of(bx.tcx().mk_box(content_ty)); - let llty_ptr = box_layout.llvm_type(bx.cx); - - // Allocate space: - let def_id = match bx.tcx().lang_items().require(ExchangeMallocFnLangItem) { - Ok(id) => id, - Err(s) => { - bx.sess().fatal(&format!("allocation of `{}` {}", box_layout.ty, s)); - } - }; - let instance = ty::Instance::mono(bx.tcx(), def_id); - let r = callee::get_fn(bx.cx, instance); - let val = bx.pointercast(bx.call(r, &[llsize, llalign], None), llty_ptr); - - let operand = OperandRef { - val: OperandValue::Immediate(val), - layout: box_layout, - }; - (bx, operand) - } - mir::Rvalue::Use(ref operand) => { - let operand = self.trans_operand(&bx, operand); - (bx, operand) - } - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) => { - // According to `rvalue_creates_operand`, only ZST - // aggregate rvalues are allowed to be operands. - let ty = rvalue.ty(self.mir, self.cx.tcx); - (bx, OperandRef::new_zst(self.cx, - self.cx.layout_of(self.monomorphize(&ty)))) - } - } - } - - fn evaluate_array_len(&mut self, - bx: &Builder<'a, 'tcx>, - place: &mir::Place<'tcx>) -> ValueRef - { - // ZST are passed as operands and require special handling - // because trans_place() panics if Local is operand. - if let mir::Place::Local(index) = *place { - if let LocalRef::Operand(Some(op)) = self.locals[index] { - if let ty::TyArray(_, n) = op.layout.ty.sty { - let n = n.unwrap_usize(bx.cx.tcx); - return common::C_usize(bx.cx, n); - } - } - } - // use common size calculation for non zero-sized types - let tr_value = self.trans_place(&bx, place); - return tr_value.len(bx.cx); - } - - pub fn trans_scalar_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty<'tcx>) -> ValueRef { - let is_float = input_ty.is_fp(); - let is_signed = input_ty.is_signed(); - let is_nil = input_ty.is_nil(); - match op { - mir::BinOp::Add => if is_float { - bx.fadd(lhs, rhs) - } else { - bx.add(lhs, rhs) - }, - mir::BinOp::Sub => if is_float { - bx.fsub(lhs, rhs) - } else { - bx.sub(lhs, rhs) - }, - mir::BinOp::Mul => if is_float { - bx.fmul(lhs, rhs) - } else { - bx.mul(lhs, rhs) - }, - mir::BinOp::Div => if is_float { - bx.fdiv(lhs, rhs) - } else if is_signed { - bx.sdiv(lhs, rhs) - } else { - bx.udiv(lhs, rhs) - }, - mir::BinOp::Rem => if is_float { - bx.frem(lhs, rhs) - } else if is_signed { - bx.srem(lhs, rhs) - } else { - bx.urem(lhs, rhs) - }, - mir::BinOp::BitOr => bx.or(lhs, rhs), - mir::BinOp::BitAnd => bx.and(lhs, rhs), - mir::BinOp::BitXor => bx.xor(lhs, rhs), - mir::BinOp::Offset => bx.inbounds_gep(lhs, &[rhs]), - mir::BinOp::Shl => common::build_unchecked_lshift(bx, lhs, rhs), - mir::BinOp::Shr => common::build_unchecked_rshift(bx, input_ty, lhs, rhs), - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt | - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => if is_nil { - C_bool(bx.cx, match op { - mir::BinOp::Ne | mir::BinOp::Lt | mir::BinOp::Gt => false, - mir::BinOp::Eq | mir::BinOp::Le | mir::BinOp::Ge => true, - _ => unreachable!() - }) - } else if is_float { - bx.fcmp( - base::bin_op_to_fcmp_predicate(op.to_hir_binop()), - lhs, rhs - ) - } else { - bx.icmp( - base::bin_op_to_icmp_predicate(op.to_hir_binop(), is_signed), - lhs, rhs - ) - } - } - } - - pub fn trans_fat_ptr_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs_addr: ValueRef, - lhs_extra: ValueRef, - rhs_addr: ValueRef, - rhs_extra: ValueRef, - _input_ty: Ty<'tcx>) - -> ValueRef { - match op { - mir::BinOp::Eq => { - bx.and( - bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), - bx.icmp(llvm::IntEQ, lhs_extra, rhs_extra) - ) - } - mir::BinOp::Ne => { - bx.or( - bx.icmp(llvm::IntNE, lhs_addr, rhs_addr), - bx.icmp(llvm::IntNE, lhs_extra, rhs_extra) - ) - } - mir::BinOp::Le | mir::BinOp::Lt | - mir::BinOp::Ge | mir::BinOp::Gt => { - // a OP b ~ a.0 STRICT(OP) b.0 | (a.0 == b.0 && a.1 OP a.1) - let (op, strict_op) = match op { - mir::BinOp::Lt => (llvm::IntULT, llvm::IntULT), - mir::BinOp::Le => (llvm::IntULE, llvm::IntULT), - mir::BinOp::Gt => (llvm::IntUGT, llvm::IntUGT), - mir::BinOp::Ge => (llvm::IntUGE, llvm::IntUGT), - _ => bug!(), - }; - - bx.or( - bx.icmp(strict_op, lhs_addr, rhs_addr), - bx.and( - bx.icmp(llvm::IntEQ, lhs_addr, rhs_addr), - bx.icmp(op, lhs_extra, rhs_extra) - ) - ) - } - _ => { - bug!("unexpected fat ptr binop"); - } - } - } - - pub fn trans_scalar_checked_binop(&mut self, - bx: &Builder<'a, 'tcx>, - op: mir::BinOp, - lhs: ValueRef, - rhs: ValueRef, - input_ty: Ty<'tcx>) -> OperandValue { - // This case can currently arise only from functions marked - // with #[rustc_inherit_overflow_checks] and inlined from - // another crate (mostly core::num generic/#[inline] fns), - // while the current crate doesn't use overflow checks. - if !bx.cx.check_overflow { - let val = self.trans_scalar_binop(bx, op, lhs, rhs, input_ty); - return OperandValue::Pair(val, C_bool(bx.cx, false)); - } - - let (val, of) = match op { - // These are checked using intrinsics - mir::BinOp::Add | mir::BinOp::Sub | mir::BinOp::Mul => { - let oop = match op { - mir::BinOp::Add => OverflowOp::Add, - mir::BinOp::Sub => OverflowOp::Sub, - mir::BinOp::Mul => OverflowOp::Mul, - _ => unreachable!() - }; - let intrinsic = get_overflow_intrinsic(oop, bx, input_ty); - let res = bx.call(intrinsic, &[lhs, rhs], None); - - (bx.extract_value(res, 0), - bx.extract_value(res, 1)) - } - mir::BinOp::Shl | mir::BinOp::Shr => { - let lhs_llty = val_ty(lhs); - let rhs_llty = val_ty(rhs); - let invert_mask = common::shift_mask_val(&bx, lhs_llty, rhs_llty, true); - let outer_bits = bx.and(rhs, invert_mask); - - let of = bx.icmp(llvm::IntNE, outer_bits, C_null(rhs_llty)); - let val = self.trans_scalar_binop(bx, op, lhs, rhs, input_ty); - - (val, of) - } - _ => { - bug!("Operator `{:?}` is not a checkable operator", op) - } - }; - - OperandValue::Pair(val, of) - } - - pub fn rvalue_creates_operand(&self, rvalue: &mir::Rvalue<'tcx>) -> bool { - match *rvalue { - mir::Rvalue::Ref(..) | - mir::Rvalue::Len(..) | - mir::Rvalue::Cast(..) | // (*) - mir::Rvalue::BinaryOp(..) | - mir::Rvalue::CheckedBinaryOp(..) | - mir::Rvalue::UnaryOp(..) | - mir::Rvalue::Discriminant(..) | - mir::Rvalue::NullaryOp(..) | - mir::Rvalue::Use(..) => // (*) - true, - mir::Rvalue::Repeat(..) | - mir::Rvalue::Aggregate(..) => { - let ty = rvalue.ty(self.mir, self.cx.tcx); - let ty = self.monomorphize(&ty); - self.cx.layout_of(ty).is_zst() - } - } - - // (*) this is only true if the type is suitable - } -} - -#[derive(Copy, Clone)] -enum OverflowOp { - Add, Sub, Mul -} - -fn get_overflow_intrinsic(oop: OverflowOp, bx: &Builder, ty: Ty) -> ValueRef { - use syntax::ast::IntTy::*; - use syntax::ast::UintTy::*; - use rustc::ty::{TyInt, TyUint}; - - let tcx = bx.tcx(); - - let new_sty = match ty.sty { - TyInt(Isize) => match &tcx.sess.target.target.target_pointer_width[..] { - "16" => TyInt(I16), - "32" => TyInt(I32), - "64" => TyInt(I64), - _ => panic!("unsupported target word size") - }, - TyUint(Usize) => match &tcx.sess.target.target.target_pointer_width[..] { - "16" => TyUint(U16), - "32" => TyUint(U32), - "64" => TyUint(U64), - _ => panic!("unsupported target word size") - }, - ref t @ TyUint(_) | ref t @ TyInt(_) => t.clone(), - _ => panic!("tried to get overflow intrinsic for op applied to non-int type") - }; - - let name = match oop { - OverflowOp::Add => match new_sty { - TyInt(I8) => "llvm.sadd.with.overflow.i8", - TyInt(I16) => "llvm.sadd.with.overflow.i16", - TyInt(I32) => "llvm.sadd.with.overflow.i32", - TyInt(I64) => "llvm.sadd.with.overflow.i64", - TyInt(I128) => "llvm.sadd.with.overflow.i128", - - TyUint(U8) => "llvm.uadd.with.overflow.i8", - TyUint(U16) => "llvm.uadd.with.overflow.i16", - TyUint(U32) => "llvm.uadd.with.overflow.i32", - TyUint(U64) => "llvm.uadd.with.overflow.i64", - TyUint(U128) => "llvm.uadd.with.overflow.i128", - - _ => unreachable!(), - }, - OverflowOp::Sub => match new_sty { - TyInt(I8) => "llvm.ssub.with.overflow.i8", - TyInt(I16) => "llvm.ssub.with.overflow.i16", - TyInt(I32) => "llvm.ssub.with.overflow.i32", - TyInt(I64) => "llvm.ssub.with.overflow.i64", - TyInt(I128) => "llvm.ssub.with.overflow.i128", - - TyUint(U8) => "llvm.usub.with.overflow.i8", - TyUint(U16) => "llvm.usub.with.overflow.i16", - TyUint(U32) => "llvm.usub.with.overflow.i32", - TyUint(U64) => "llvm.usub.with.overflow.i64", - TyUint(U128) => "llvm.usub.with.overflow.i128", - - _ => unreachable!(), - }, - OverflowOp::Mul => match new_sty { - TyInt(I8) => "llvm.smul.with.overflow.i8", - TyInt(I16) => "llvm.smul.with.overflow.i16", - TyInt(I32) => "llvm.smul.with.overflow.i32", - TyInt(I64) => "llvm.smul.with.overflow.i64", - TyInt(I128) => "llvm.smul.with.overflow.i128", - - TyUint(U8) => "llvm.umul.with.overflow.i8", - TyUint(U16) => "llvm.umul.with.overflow.i16", - TyUint(U32) => "llvm.umul.with.overflow.i32", - TyUint(U64) => "llvm.umul.with.overflow.i64", - TyUint(U128) => "llvm.umul.with.overflow.i128", - - _ => unreachable!(), - }, - }; - - bx.cx.get_intrinsic(&name) -} - -fn cast_int_to_float(bx: &Builder, - signed: bool, - x: ValueRef, - int_ty: Type, - float_ty: Type) -> ValueRef { - // Most integer types, even i128, fit into [-f32::MAX, f32::MAX] after rounding. - // It's only u128 -> f32 that can cause overflows (i.e., should yield infinity). - // LLVM's uitofp produces undef in those cases, so we manually check for that case. - let is_u128_to_f32 = !signed && int_ty.int_width() == 128 && float_ty.float_width() == 32; - if is_u128_to_f32 { - // All inputs greater or equal to (f32::MAX + 0.5 ULP) are rounded to infinity, - // and for everything else LLVM's uitofp works just fine. - use rustc_apfloat::ieee::Single; - use rustc_apfloat::Float; - const MAX_F32_PLUS_HALF_ULP: u128 = ((1 << (Single::PRECISION + 1)) - 1) - << (Single::MAX_EXP - Single::PRECISION as i16); - let max = C_uint_big(int_ty, MAX_F32_PLUS_HALF_ULP); - let overflow = bx.icmp(llvm::IntUGE, x, max); - let infinity_bits = C_u32(bx.cx, ieee::Single::INFINITY.to_bits() as u32); - let infinity = consts::bitcast(infinity_bits, float_ty); - bx.select(overflow, infinity, bx.uitofp(x, float_ty)) - } else { - if signed { - bx.sitofp(x, float_ty) - } else { - bx.uitofp(x, float_ty) - } - } -} - -fn cast_float_to_int(bx: &Builder, - signed: bool, - x: ValueRef, - float_ty: Type, - int_ty: Type) -> ValueRef { - let fptosui_result = if signed { - bx.fptosi(x, int_ty) - } else { - bx.fptoui(x, int_ty) - }; - - if !bx.sess().opts.debugging_opts.saturating_float_casts { - return fptosui_result; - } - // LLVM's fpto[su]i returns undef when the input x is infinite, NaN, or does not fit into the - // destination integer type after rounding towards zero. This `undef` value can cause UB in - // safe code (see issue #10184), so we implement a saturating conversion on top of it: - // Semantically, the mathematical value of the input is rounded towards zero to the next - // mathematical integer, and then the result is clamped into the range of the destination - // integer type. Positive and negative infinity are mapped to the maximum and minimum value of - // the destination integer type. NaN is mapped to 0. - // - // Define f_min and f_max as the largest and smallest (finite) floats that are exactly equal to - // a value representable in int_ty. - // They are exactly equal to int_ty::{MIN,MAX} if float_ty has enough significand bits. - // Otherwise, int_ty::MAX must be rounded towards zero, as it is one less than a power of two. - // int_ty::MIN, however, is either zero or a negative power of two and is thus exactly - // representable. Note that this only works if float_ty's exponent range is sufficiently large. - // f16 or 256 bit integers would break this property. Right now the smallest float type is f32 - // with exponents ranging up to 127, which is barely enough for i128::MIN = -2^127. - // On the other hand, f_max works even if int_ty::MAX is greater than float_ty::MAX. Because - // we're rounding towards zero, we just get float_ty::MAX (which is always an integer). - // This already happens today with u128::MAX = 2^128 - 1 > f32::MAX. - fn compute_clamp_bounds(signed: bool, int_ty: Type) -> (u128, u128) { - let rounded_min = F::from_i128_r(int_min(signed, int_ty), Round::TowardZero); - assert_eq!(rounded_min.status, Status::OK); - let rounded_max = F::from_u128_r(int_max(signed, int_ty), Round::TowardZero); - assert!(rounded_max.value.is_finite()); - (rounded_min.value.to_bits(), rounded_max.value.to_bits()) - } - fn int_max(signed: bool, int_ty: Type) -> u128 { - let shift_amount = 128 - int_ty.int_width(); - if signed { - i128::MAX as u128 >> shift_amount - } else { - u128::MAX >> shift_amount - } - } - fn int_min(signed: bool, int_ty: Type) -> i128 { - if signed { - i128::MIN >> (128 - int_ty.int_width()) - } else { - 0 - } - } - let float_bits_to_llval = |bits| { - let bits_llval = match float_ty.float_width() { - 32 => C_u32(bx.cx, bits as u32), - 64 => C_u64(bx.cx, bits as u64), - n => bug!("unsupported float width {}", n), - }; - consts::bitcast(bits_llval, float_ty) - }; - let (f_min, f_max) = match float_ty.float_width() { - 32 => compute_clamp_bounds::(signed, int_ty), - 64 => compute_clamp_bounds::(signed, int_ty), - n => bug!("unsupported float width {}", n), - }; - let f_min = float_bits_to_llval(f_min); - let f_max = float_bits_to_llval(f_max); - // To implement saturation, we perform the following steps: - // - // 1. Cast x to an integer with fpto[su]i. This may result in undef. - // 2. Compare x to f_min and f_max, and use the comparison results to select: - // a) int_ty::MIN if x < f_min or x is NaN - // b) int_ty::MAX if x > f_max - // c) the result of fpto[su]i otherwise - // 3. If x is NaN, return 0.0, otherwise return the result of step 2. - // - // This avoids resulting undef because values in range [f_min, f_max] by definition fit into the - // destination type. It creates an undef temporary, but *producing* undef is not UB. Our use of - // undef does not introduce any non-determinism either. - // More importantly, the above procedure correctly implements saturating conversion. - // Proof (sketch): - // If x is NaN, 0 is returned by definition. - // Otherwise, x is finite or infinite and thus can be compared with f_min and f_max. - // This yields three cases to consider: - // (1) if x in [f_min, f_max], the result of fpto[su]i is returned, which agrees with - // saturating conversion for inputs in that range. - // (2) if x > f_max, then x is larger than int_ty::MAX. This holds even if f_max is rounded - // (i.e., if f_max < int_ty::MAX) because in those cases, nextUp(f_max) is already larger - // than int_ty::MAX. Because x is larger than int_ty::MAX, the return value of int_ty::MAX - // is correct. - // (3) if x < f_min, then x is smaller than int_ty::MIN. As shown earlier, f_min exactly equals - // int_ty::MIN and therefore the return value of int_ty::MIN is correct. - // QED. - - // Step 1 was already performed above. - - // Step 2: We use two comparisons and two selects, with %s1 being the result: - // %less_or_nan = fcmp ult %x, %f_min - // %greater = fcmp olt %x, %f_max - // %s0 = select %less_or_nan, int_ty::MIN, %fptosi_result - // %s1 = select %greater, int_ty::MAX, %s0 - // Note that %less_or_nan uses an *unordered* comparison. This comparison is true if the - // operands are not comparable (i.e., if x is NaN). The unordered comparison ensures that s1 - // becomes int_ty::MIN if x is NaN. - // Performance note: Unordered comparison can be lowered to a "flipped" comparison and a - // negation, and the negation can be merged into the select. Therefore, it not necessarily any - // more expensive than a ordered ("normal") comparison. Whether these optimizations will be - // performed is ultimately up to the backend, but at least x86 does perform them. - let less_or_nan = bx.fcmp(llvm::RealULT, x, f_min); - let greater = bx.fcmp(llvm::RealOGT, x, f_max); - let int_max = C_uint_big(int_ty, int_max(signed, int_ty)); - let int_min = C_uint_big(int_ty, int_min(signed, int_ty) as u128); - let s0 = bx.select(less_or_nan, int_min, fptosui_result); - let s1 = bx.select(greater, int_max, s0); - - // Step 3: NaN replacement. - // For unsigned types, the above step already yielded int_ty::MIN == 0 if x is NaN. - // Therefore we only need to execute this step for signed integer types. - if signed { - // LLVM has no isNaN predicate, so we use (x == x) instead - bx.select(bx.fcmp(llvm::RealOEQ, x, x), s1, C_uint(int_ty, 0)) - } else { - s1 - } -} diff --git a/src/librustc_trans/mir/statement.rs b/src/librustc_trans/mir/statement.rs deleted file mode 100644 index 579b07929a2..00000000000 --- a/src/librustc_trans/mir/statement.rs +++ /dev/null @@ -1,91 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::mir; - -use asm; -use builder::Builder; - -use super::FunctionCx; -use super::LocalRef; - -impl<'a, 'tcx> FunctionCx<'a, 'tcx> { - pub fn trans_statement(&mut self, - bx: Builder<'a, 'tcx>, - statement: &mir::Statement<'tcx>) - -> Builder<'a, 'tcx> { - debug!("trans_statement(statement={:?})", statement); - - self.set_debug_loc(&bx, statement.source_info); - match statement.kind { - mir::StatementKind::Assign(ref place, ref rvalue) => { - if let mir::Place::Local(index) = *place { - match self.locals[index] { - LocalRef::Place(tr_dest) => { - self.trans_rvalue(bx, tr_dest, rvalue) - } - LocalRef::Operand(None) => { - let (bx, operand) = self.trans_rvalue_operand(bx, rvalue); - self.locals[index] = LocalRef::Operand(Some(operand)); - bx - } - LocalRef::Operand(Some(op)) => { - if !op.layout.is_zst() { - span_bug!(statement.source_info.span, - "operand {:?} already assigned", - rvalue); - } - - // If the type is zero-sized, it's already been set here, - // but we still need to make sure we translate the operand - self.trans_rvalue_operand(bx, rvalue).0 - } - } - } else { - let tr_dest = self.trans_place(&bx, place); - self.trans_rvalue(bx, tr_dest, rvalue) - } - } - mir::StatementKind::SetDiscriminant{ref place, variant_index} => { - self.trans_place(&bx, place) - .trans_set_discr(&bx, variant_index); - bx - } - mir::StatementKind::StorageLive(local) => { - if let LocalRef::Place(tr_place) = self.locals[local] { - tr_place.storage_live(&bx); - } - bx - } - mir::StatementKind::StorageDead(local) => { - if let LocalRef::Place(tr_place) = self.locals[local] { - tr_place.storage_dead(&bx); - } - bx - } - mir::StatementKind::InlineAsm { ref asm, ref outputs, ref inputs } => { - let outputs = outputs.iter().map(|output| { - self.trans_place(&bx, output) - }).collect(); - - let input_vals = inputs.iter().map(|input| { - self.trans_operand(&bx, input).immediate() - }).collect(); - - asm::trans_inline_asm(&bx, asm, outputs, input_vals); - bx - } - mir::StatementKind::EndRegion(_) | - mir::StatementKind::Validate(..) | - mir::StatementKind::UserAssertTy(..) | - mir::StatementKind::Nop => bx, - } - } -} diff --git a/src/librustc_trans/time_graph.rs b/src/librustc_trans/time_graph.rs deleted file mode 100644 index a8502682a80..00000000000 --- a/src/librustc_trans/time_graph.rs +++ /dev/null @@ -1,278 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use std::collections::HashMap; -use std::fs::File; -use std::io::prelude::*; -use std::marker::PhantomData; -use std::mem; -use std::sync::{Arc, Mutex}; -use std::time::Instant; - -const OUTPUT_WIDTH_IN_PX: u64 = 1000; -const TIME_LINE_HEIGHT_IN_PX: u64 = 20; -const TIME_LINE_HEIGHT_STRIDE_IN_PX: usize = 30; - -#[derive(Clone)] -struct Timing { - start: Instant, - end: Instant, - work_package_kind: WorkPackageKind, - name: String, - events: Vec<(String, Instant)>, -} - -#[derive(Clone, Copy, Hash, Eq, PartialEq, Debug)] -pub struct TimelineId(pub usize); - -#[derive(Clone)] -struct PerThread { - timings: Vec, - open_work_package: Option<(Instant, WorkPackageKind, String)>, -} - -#[derive(Clone)] -pub struct TimeGraph { - data: Arc>>, -} - -#[derive(Clone, Copy)] -pub struct WorkPackageKind(pub &'static [&'static str]); - -pub struct Timeline { - token: Option, -} - -struct RaiiToken { - graph: TimeGraph, - timeline: TimelineId, - events: Vec<(String, Instant)>, - // The token must not be Send: - _marker: PhantomData<*const ()> -} - - -impl Drop for RaiiToken { - fn drop(&mut self) { - self.graph.end(self.timeline, mem::replace(&mut self.events, Vec::new())); - } -} - -impl TimeGraph { - pub fn new() -> TimeGraph { - TimeGraph { - data: Arc::new(Mutex::new(HashMap::new())) - } - } - - pub fn start(&self, - timeline: TimelineId, - work_package_kind: WorkPackageKind, - name: &str) -> Timeline { - { - let mut table = self.data.lock().unwrap(); - - let data = table.entry(timeline).or_insert(PerThread { - timings: Vec::new(), - open_work_package: None, - }); - - assert!(data.open_work_package.is_none()); - data.open_work_package = Some((Instant::now(), work_package_kind, name.to_string())); - } - - Timeline { - token: Some(RaiiToken { - graph: self.clone(), - timeline, - events: Vec::new(), - _marker: PhantomData, - }), - } - } - - fn end(&self, timeline: TimelineId, events: Vec<(String, Instant)>) { - let end = Instant::now(); - - let mut table = self.data.lock().unwrap(); - let data = table.get_mut(&timeline).unwrap(); - - if let Some((start, work_package_kind, name)) = data.open_work_package.take() { - data.timings.push(Timing { - start, - end, - work_package_kind, - name, - events, - }); - } else { - bug!("end timing without start?") - } - } - - pub fn dump(&self, output_filename: &str) { - let table = self.data.lock().unwrap(); - - for data in table.values() { - assert!(data.open_work_package.is_none()); - } - - let mut threads: Vec = - table.values().map(|data| data.clone()).collect(); - - threads.sort_by_key(|timeline| timeline.timings[0].start); - - let earliest_instant = threads[0].timings[0].start; - let latest_instant = threads.iter() - .map(|timeline| timeline.timings - .last() - .unwrap() - .end) - .max() - .unwrap(); - let max_distance = distance(earliest_instant, latest_instant); - - let mut file = File::create(format!("{}.html", output_filename)).unwrap(); - - writeln!(file, " - - - - - -
- ", - total_height = threads.len() * TIME_LINE_HEIGHT_STRIDE_IN_PX, - width = OUTPUT_WIDTH_IN_PX, - ).unwrap(); - - let mut color = 0; - for (line_index, thread) in threads.iter().enumerate() { - let line_top = line_index * TIME_LINE_HEIGHT_STRIDE_IN_PX; - - for span in &thread.timings { - let start = distance(earliest_instant, span.start); - let end = distance(earliest_instant, span.end); - - let start = normalize(start, max_distance, OUTPUT_WIDTH_IN_PX); - let end = normalize(end, max_distance, OUTPUT_WIDTH_IN_PX); - - let colors = span.work_package_kind.0; - - writeln!(file, "{}", - color, - line_top, - start, - end - start, - TIME_LINE_HEIGHT_IN_PX, - colors[color % colors.len()], - span.name, - ).unwrap(); - - color += 1; - } - } - - writeln!(file, " -
- ").unwrap(); - - let mut idx = 0; - for thread in threads.iter() { - for timing in &thread.timings { - let colors = timing.work_package_kind.0; - let height = TIME_LINE_HEIGHT_STRIDE_IN_PX * timing.events.len(); - writeln!(file, "
", - idx, - colors[idx % colors.len()], - height).unwrap(); - idx += 1; - let max = distance(timing.start, timing.end); - for (i, &(ref event, time)) in timing.events.iter().enumerate() { - let i = i as u64; - let time = distance(timing.start, time); - let at = normalize(time, max, OUTPUT_WIDTH_IN_PX); - writeln!(file, "{}", - at, - TIME_LINE_HEIGHT_IN_PX * i, - event).unwrap(); - } - writeln!(file, "
").unwrap(); - } - } - - writeln!(file, " - - - ").unwrap(); - } -} - -impl Timeline { - pub fn noop() -> Timeline { - Timeline { token: None } - } - - /// Record an event which happened at this moment on this timeline. - /// - /// Events are displayed in the eventual HTML output where you can click on - /// a particular timeline and it'll expand to all of the events that - /// happened on that timeline. This can then be used to drill into a - /// particular timeline and see what events are happening and taking the - /// most time. - pub fn record(&mut self, name: &str) { - if let Some(ref mut token) = self.token { - token.events.push((name.to_string(), Instant::now())); - } - } -} - -fn distance(zero: Instant, x: Instant) -> u64 { - - let duration = x.duration_since(zero); - (duration.as_secs() * 1_000_000_000 + duration.subsec_nanos() as u64) // / div -} - -fn normalize(distance: u64, max: u64, max_pixels: u64) -> u64 { - (max_pixels * distance) / max -} - diff --git a/src/librustc_trans/trans_item.rs b/src/librustc_trans/trans_item.rs deleted file mode 100644 index d19b5af2527..00000000000 --- a/src/librustc_trans/trans_item.rs +++ /dev/null @@ -1,193 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_item_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! item-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use asm; -use attributes; -use base; -use consts; -use context::CodegenCx; -use declare; -use llvm; -use monomorphize::Instance; -use type_of::LayoutLlvmExt; -use rustc::hir; -use rustc::hir::def::Def; -use rustc::hir::def_id::DefId; -use rustc::mir::mono::{Linkage, Visibility}; -use rustc::ty::TypeFoldable; -use rustc::ty::layout::LayoutOf; -use syntax::attr; -use std::fmt; - -pub use rustc::mir::mono::MonoItem; - -pub use rustc_mir::monomorphize::item::*; -pub use rustc_mir::monomorphize::item::MonoItemExt as BaseMonoItemExt; - -pub trait MonoItemExt<'a, 'tcx>: fmt::Debug + BaseMonoItemExt<'a, 'tcx> { - fn define(&self, cx: &CodegenCx<'a, 'tcx>) { - debug!("BEGIN IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - - match *self.as_mono_item() { - MonoItem::Static(def_id) => { - let tcx = cx.tcx; - let is_mutable = match tcx.describe_def(def_id) { - Some(Def::Static(_, is_mutable)) => is_mutable, - Some(other) => { - bug!("Expected Def::Static, found {:?}", other) - } - None => { - bug!("Expected Def::Static for {:?}, found nothing", def_id) - } - }; - let attrs = tcx.get_attrs(def_id); - - consts::trans_static(&cx, def_id, is_mutable, &attrs); - } - MonoItem::GlobalAsm(node_id) => { - let item = cx.tcx.hir.expect_item(node_id); - if let hir::ItemGlobalAsm(ref ga) = item.node { - asm::trans_global_asm(cx, ga); - } else { - span_bug!(item.span, "Mismatch between hir::Item type and TransItem type") - } - } - MonoItem::Fn(instance) => { - base::trans_instance(&cx, instance); - } - } - - debug!("END IMPLEMENTING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - } - - fn predefine(&self, - cx: &CodegenCx<'a, 'tcx>, - linkage: Linkage, - visibility: Visibility) { - debug!("BEGIN PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - - let symbol_name = self.symbol_name(cx.tcx).as_str(); - - debug!("symbol {}", &symbol_name); - - match *self.as_mono_item() { - MonoItem::Static(def_id) => { - predefine_static(cx, def_id, linkage, visibility, &symbol_name); - } - MonoItem::Fn(instance) => { - predefine_fn(cx, instance, linkage, visibility, &symbol_name); - } - MonoItem::GlobalAsm(..) => {} - } - - debug!("END PREDEFINING '{} ({})' in cgu {}", - self.to_string(cx.tcx), - self.to_raw_string(), - cx.codegen_unit.name()); - } - - fn to_raw_string(&self) -> String { - match *self.as_mono_item() { - MonoItem::Fn(instance) => { - format!("Fn({:?}, {})", - instance.def, - instance.substs.as_ptr() as usize) - } - MonoItem::Static(id) => { - format!("Static({:?})", id) - } - MonoItem::GlobalAsm(id) => { - format!("GlobalAsm({:?})", id) - } - } - } -} - -impl<'a, 'tcx> MonoItemExt<'a, 'tcx> for MonoItem<'tcx> {} - -fn predefine_static<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - def_id: DefId, - linkage: Linkage, - visibility: Visibility, - symbol_name: &str) { - let instance = Instance::mono(cx.tcx, def_id); - let ty = instance.ty(cx.tcx); - let llty = cx.layout_of(ty).llvm_type(cx); - - let g = declare::define_global(cx, symbol_name, llty).unwrap_or_else(|| { - cx.sess().span_fatal(cx.tcx.def_span(def_id), - &format!("symbol `{}` is already defined", symbol_name)) - }); - - unsafe { - llvm::LLVMRustSetLinkage(g, base::linkage_to_llvm(linkage)); - llvm::LLVMRustSetVisibility(g, base::visibility_to_llvm(visibility)); - } - - cx.instances.borrow_mut().insert(instance, g); - cx.statics.borrow_mut().insert(g, def_id); -} - -fn predefine_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - instance: Instance<'tcx>, - linkage: Linkage, - visibility: Visibility, - symbol_name: &str) { - assert!(!instance.substs.needs_infer() && - !instance.substs.has_param_types()); - - let mono_ty = instance.ty(cx.tcx); - let attrs = instance.def.attrs(cx.tcx); - let lldecl = declare::declare_fn(cx, symbol_name, mono_ty); - unsafe { llvm::LLVMRustSetLinkage(lldecl, base::linkage_to_llvm(linkage)) }; - base::set_link_section(cx, lldecl, &attrs); - if linkage == Linkage::LinkOnceODR || - linkage == Linkage::WeakODR { - llvm::SetUniqueComdat(cx.llmod, lldecl); - } - - // If we're compiling the compiler-builtins crate, e.g. the equivalent of - // compiler-rt, then we want to implicitly compile everything with hidden - // visibility as we're going to link this object all over the place but - // don't want the symbols to get exported. - if linkage != Linkage::Internal && linkage != Linkage::Private && - attr::contains_name(cx.tcx.hir.krate_attrs(), "compiler_builtins") { - unsafe { - llvm::LLVMRustSetVisibility(lldecl, llvm::Visibility::Hidden); - } - } else { - unsafe { - llvm::LLVMRustSetVisibility(lldecl, base::visibility_to_llvm(visibility)); - } - } - - debug!("predefine_fn: mono_ty = {:?} instance = {:?}", mono_ty, instance); - if instance.def.is_inline(cx.tcx) { - attributes::inline(lldecl, attributes::InlineAttr::Hint); - } - attributes::from_fn_attrs(cx, lldecl, instance.def.def_id()); - - cx.instances.borrow_mut().insert(instance, lldecl); -} diff --git a/src/librustc_trans/type_.rs b/src/librustc_trans/type_.rs deleted file mode 100644 index a77acc4f175..00000000000 --- a/src/librustc_trans/type_.rs +++ /dev/null @@ -1,300 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(non_upper_case_globals)] - -use llvm; -use llvm::{ContextRef, TypeRef, Bool, False, True, TypeKind}; -use llvm::{Float, Double, X86_FP80, PPC_FP128, FP128}; - -use context::CodegenCx; - -use syntax::ast; -use rustc::ty::layout::{self, Align, Size}; - -use std::ffi::CString; -use std::fmt; -use std::mem; -use std::ptr; - -use libc::c_uint; - -#[derive(Clone, Copy, PartialEq)] -#[repr(C)] -pub struct Type { - rf: TypeRef -} - -impl fmt::Debug for Type { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&llvm::build_string(|s| unsafe { - llvm::LLVMRustWriteTypeToString(self.to_ref(), s); - }).expect("non-UTF8 type description from LLVM")) - } -} - -macro_rules! ty { - ($e:expr) => ( Type::from_ref(unsafe { $e })) -} - -/// Wrapper for LLVM TypeRef -impl Type { - #[inline(always)] - pub fn from_ref(r: TypeRef) -> Type { - Type { - rf: r - } - } - - #[inline(always)] // So it doesn't kill --opt-level=0 builds of the compiler - pub fn to_ref(&self) -> TypeRef { - self.rf - } - - pub fn to_ref_slice(slice: &[Type]) -> &[TypeRef] { - unsafe { mem::transmute(slice) } - } - - pub fn void(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMVoidTypeInContext(cx.llcx)) - } - - pub fn metadata(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMRustMetadataTypeInContext(cx.llcx)) - } - - pub fn i1(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt1TypeInContext(cx.llcx)) - } - - pub fn i8(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt8TypeInContext(cx.llcx)) - } - - pub fn i8_llcx(llcx: ContextRef) -> Type { - ty!(llvm::LLVMInt8TypeInContext(llcx)) - } - - pub fn i16(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt16TypeInContext(cx.llcx)) - } - - pub fn i32(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt32TypeInContext(cx.llcx)) - } - - pub fn i64(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMInt64TypeInContext(cx.llcx)) - } - - pub fn i128(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMIntTypeInContext(cx.llcx, 128)) - } - - // Creates an integer type with the given number of bits, e.g. i24 - pub fn ix(cx: &CodegenCx, num_bits: u64) -> Type { - ty!(llvm::LLVMIntTypeInContext(cx.llcx, num_bits as c_uint)) - } - - pub fn f32(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMFloatTypeInContext(cx.llcx)) - } - - pub fn f64(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMDoubleTypeInContext(cx.llcx)) - } - - pub fn bool(cx: &CodegenCx) -> Type { - Type::i8(cx) - } - - pub fn char(cx: &CodegenCx) -> Type { - Type::i32(cx) - } - - pub fn i8p(cx: &CodegenCx) -> Type { - Type::i8(cx).ptr_to() - } - - pub fn i8p_llcx(llcx: ContextRef) -> Type { - Type::i8_llcx(llcx).ptr_to() - } - - pub fn isize(cx: &CodegenCx) -> Type { - match &cx.tcx.sess.target.target.target_pointer_width[..] { - "16" => Type::i16(cx), - "32" => Type::i32(cx), - "64" => Type::i64(cx), - tws => bug!("Unsupported target word size for int: {}", tws), - } - } - - pub fn c_int(cx: &CodegenCx) -> Type { - match &cx.tcx.sess.target.target.target_c_int_width[..] { - "16" => Type::i16(cx), - "32" => Type::i32(cx), - "64" => Type::i64(cx), - width => bug!("Unsupported target_c_int_width: {}", width), - } - } - - pub fn int_from_ty(cx: &CodegenCx, t: ast::IntTy) -> Type { - match t { - ast::IntTy::Isize => cx.isize_ty, - ast::IntTy::I8 => Type::i8(cx), - ast::IntTy::I16 => Type::i16(cx), - ast::IntTy::I32 => Type::i32(cx), - ast::IntTy::I64 => Type::i64(cx), - ast::IntTy::I128 => Type::i128(cx), - } - } - - pub fn uint_from_ty(cx: &CodegenCx, t: ast::UintTy) -> Type { - match t { - ast::UintTy::Usize => cx.isize_ty, - ast::UintTy::U8 => Type::i8(cx), - ast::UintTy::U16 => Type::i16(cx), - ast::UintTy::U32 => Type::i32(cx), - ast::UintTy::U64 => Type::i64(cx), - ast::UintTy::U128 => Type::i128(cx), - } - } - - pub fn float_from_ty(cx: &CodegenCx, t: ast::FloatTy) -> Type { - match t { - ast::FloatTy::F32 => Type::f32(cx), - ast::FloatTy::F64 => Type::f64(cx), - } - } - - pub fn func(args: &[Type], ret: &Type) -> Type { - let slice: &[TypeRef] = Type::to_ref_slice(args); - ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), - args.len() as c_uint, False)) - } - - pub fn variadic_func(args: &[Type], ret: &Type) -> Type { - let slice: &[TypeRef] = Type::to_ref_slice(args); - ty!(llvm::LLVMFunctionType(ret.to_ref(), slice.as_ptr(), - args.len() as c_uint, True)) - } - - pub fn struct_(cx: &CodegenCx, els: &[Type], packed: bool) -> Type { - let els: &[TypeRef] = Type::to_ref_slice(els); - ty!(llvm::LLVMStructTypeInContext(cx.llcx, els.as_ptr(), - els.len() as c_uint, - packed as Bool)) - } - - pub fn named_struct(cx: &CodegenCx, name: &str) -> Type { - let name = CString::new(name).unwrap(); - ty!(llvm::LLVMStructCreateNamed(cx.llcx, name.as_ptr())) - } - - - pub fn array(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMRustArrayType(ty.to_ref(), len)) - } - - pub fn vector(ty: &Type, len: u64) -> Type { - ty!(llvm::LLVMVectorType(ty.to_ref(), len as c_uint)) - } - - pub fn kind(&self) -> TypeKind { - unsafe { - llvm::LLVMRustGetTypeKind(self.to_ref()) - } - } - - pub fn set_struct_body(&mut self, els: &[Type], packed: bool) { - let slice: &[TypeRef] = Type::to_ref_slice(els); - unsafe { - llvm::LLVMStructSetBody(self.to_ref(), slice.as_ptr(), - els.len() as c_uint, packed as Bool) - } - } - - pub fn ptr_to(&self) -> Type { - ty!(llvm::LLVMPointerType(self.to_ref(), 0)) - } - - pub fn element_type(&self) -> Type { - unsafe { - Type::from_ref(llvm::LLVMGetElementType(self.to_ref())) - } - } - - /// Return the number of elements in `self` if it is a LLVM vector type. - pub fn vector_length(&self) -> usize { - unsafe { - llvm::LLVMGetVectorSize(self.to_ref()) as usize - } - } - - pub fn func_params(&self) -> Vec { - unsafe { - let n_args = llvm::LLVMCountParamTypes(self.to_ref()) as usize; - let mut args = vec![Type { rf: ptr::null_mut() }; n_args]; - llvm::LLVMGetParamTypes(self.to_ref(), - args.as_mut_ptr() as *mut TypeRef); - args - } - } - - pub fn float_width(&self) -> usize { - match self.kind() { - Float => 32, - Double => 64, - X86_FP80 => 80, - FP128 | PPC_FP128 => 128, - _ => bug!("llvm_float_width called on a non-float type") - } - } - - /// Retrieve the bit width of the integer type `self`. - pub fn int_width(&self) -> u64 { - unsafe { - llvm::LLVMGetIntTypeWidth(self.to_ref()) as u64 - } - } - - pub fn from_integer(cx: &CodegenCx, i: layout::Integer) -> Type { - use rustc::ty::layout::Integer::*; - match i { - I8 => Type::i8(cx), - I16 => Type::i16(cx), - I32 => Type::i32(cx), - I64 => Type::i64(cx), - I128 => Type::i128(cx), - } - } - - /// Return a LLVM type that has at most the required alignment, - /// as a conservative approximation for unknown pointee types. - pub fn pointee_for_abi_align(cx: &CodegenCx, align: Align) -> Type { - // FIXME(eddyb) We could find a better approximation if ity.align < align. - let ity = layout::Integer::approximate_abi_align(cx, align); - Type::from_integer(cx, ity) - } - - /// Return a LLVM type that has at most the required alignment, - /// and exactly the required size, as a best-effort padding array. - pub fn padding_filler(cx: &CodegenCx, size: Size, align: Align) -> Type { - let unit = layout::Integer::approximate_abi_align(cx, align); - let size = size.bytes(); - let unit_size = unit.size().bytes(); - assert_eq!(size % unit_size, 0); - Type::array(&Type::from_integer(cx, unit), size / unit_size) - } - - pub fn x86_mmx(cx: &CodegenCx) -> Type { - ty!(llvm::LLVMX86MMXTypeInContext(cx.llcx)) - } -} diff --git a/src/librustc_trans/type_of.rs b/src/librustc_trans/type_of.rs deleted file mode 100644 index 32d26052aff..00000000000 --- a/src/librustc_trans/type_of.rs +++ /dev/null @@ -1,506 +0,0 @@ -// Copyright 2012-2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use abi::{FnType, FnTypeExt}; -use common::*; -use rustc::hir; -use rustc::ty::{self, Ty, TypeFoldable}; -use rustc::ty::layout::{self, Align, LayoutOf, Size, TyLayout}; -use rustc_target::spec::PanicStrategy; -use trans_item::DefPathBasedNames; -use type_::Type; - -use std::fmt::Write; - -fn uncached_llvm_type<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>, - defer: &mut Option<(Type, TyLayout<'tcx>)>) - -> Type { - match layout.abi { - layout::Abi::Scalar(_) => bug!("handled elsewhere"), - layout::Abi::Vector { ref element, count } => { - // LLVM has a separate type for 64-bit SIMD vectors on X86 called - // `x86_mmx` which is needed for some SIMD operations. As a bit of a - // hack (all SIMD definitions are super unstable anyway) we - // recognize any one-element SIMD vector as "this should be an - // x86_mmx" type. In general there shouldn't be a need for other - // one-element SIMD vectors, so it's assumed this won't clash with - // much else. - let use_x86_mmx = count == 1 && layout.size.bits() == 64 && - (cx.sess().target.target.arch == "x86" || - cx.sess().target.target.arch == "x86_64"); - if use_x86_mmx { - return Type::x86_mmx(cx) - } else { - let element = layout.scalar_llvm_type_at(cx, element, Size::from_bytes(0)); - return Type::vector(&element, count); - } - } - layout::Abi::ScalarPair(..) => { - return Type::struct_(cx, &[ - layout.scalar_pair_element_llvm_type(cx, 0), - layout.scalar_pair_element_llvm_type(cx, 1), - ], false); - } - layout::Abi::Uninhabited | - layout::Abi::Aggregate { .. } => {} - } - - let name = match layout.ty.sty { - ty::TyClosure(..) | - ty::TyGenerator(..) | - ty::TyAdt(..) | - // FIXME(eddyb) producing readable type names for trait objects can result - // in problematically distinct types due to HRTB and subtyping (see #47638). - // ty::TyDynamic(..) | - ty::TyForeign(..) | - ty::TyStr => { - let mut name = String::with_capacity(32); - let printer = DefPathBasedNames::new(cx.tcx, true, true); - printer.push_type_name(layout.ty, &mut name); - match (&layout.ty.sty, &layout.variants) { - (&ty::TyAdt(def, _), &layout::Variants::Single { index }) => { - if def.is_enum() && !def.variants.is_empty() { - write!(&mut name, "::{}", def.variants[index].name).unwrap(); - } - } - _ => {} - } - Some(name) - } - _ => None - }; - - match layout.fields { - layout::FieldPlacement::Union(_) => { - let fill = Type::padding_filler(cx, layout.size, layout.align); - let packed = false; - match name { - None => { - Type::struct_(cx, &[fill], packed) - } - Some(ref name) => { - let mut llty = Type::named_struct(cx, name); - llty.set_struct_body(&[fill], packed); - llty - } - } - } - layout::FieldPlacement::Array { count, .. } => { - Type::array(&layout.field(cx, 0).llvm_type(cx), count) - } - layout::FieldPlacement::Arbitrary { .. } => { - match name { - None => { - let (llfields, packed) = struct_llfields(cx, layout); - Type::struct_(cx, &llfields, packed) - } - Some(ref name) => { - let llty = Type::named_struct(cx, name); - *defer = Some((llty, layout)); - llty - } - } - } - } -} - -fn struct_llfields<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, - layout: TyLayout<'tcx>) - -> (Vec, bool) { - debug!("struct_llfields: {:#?}", layout); - let field_count = layout.fields.count(); - - let mut packed = false; - let mut offset = Size::from_bytes(0); - let mut prev_align = layout.align; - let mut result: Vec = Vec::with_capacity(1 + field_count * 2); - for i in layout.fields.index_by_increasing_offset() { - let field = layout.field(cx, i); - packed |= layout.align.abi() < field.align.abi(); - - let target_offset = layout.fields.offset(i as usize); - debug!("struct_llfields: {}: {:?} offset: {:?} target_offset: {:?}", - i, field, offset, target_offset); - assert!(target_offset >= offset); - let padding = target_offset - offset; - let padding_align = layout.align.min(prev_align).min(field.align); - assert_eq!(offset.abi_align(padding_align) + padding, target_offset); - result.push(Type::padding_filler(cx, padding, padding_align)); - debug!(" padding before: {:?}", padding); - - result.push(field.llvm_type(cx)); - offset = target_offset + field.size; - prev_align = field.align; - } - if !layout.is_unsized() && field_count > 0 { - if offset > layout.size { - bug!("layout: {:#?} stride: {:?} offset: {:?}", - layout, layout.size, offset); - } - let padding = layout.size - offset; - let padding_align = layout.align.min(prev_align); - assert_eq!(offset.abi_align(padding_align) + padding, layout.size); - debug!("struct_llfields: pad_bytes: {:?} offset: {:?} stride: {:?}", - padding, offset, layout.size); - result.push(Type::padding_filler(cx, padding, padding_align)); - assert!(result.len() == 1 + field_count * 2); - } else { - debug!("struct_llfields: offset: {:?} stride: {:?}", - offset, layout.size); - } - - (result, packed) -} - -impl<'a, 'tcx> CodegenCx<'a, 'tcx> { - pub fn align_of(&self, ty: Ty<'tcx>) -> Align { - self.layout_of(ty).align - } - - pub fn size_of(&self, ty: Ty<'tcx>) -> Size { - self.layout_of(ty).size - } - - pub fn size_and_align_of(&self, ty: Ty<'tcx>) -> (Size, Align) { - self.layout_of(ty).size_and_align() - } -} - -#[derive(Copy, Clone, PartialEq, Eq)] -pub enum PointerKind { - /// Most general case, we know no restrictions to tell LLVM. - Shared, - - /// `&T` where `T` contains no `UnsafeCell`, is `noalias` and `readonly`. - Frozen, - - /// `&mut T`, when we know `noalias` is safe for LLVM. - UniqueBorrowed, - - /// `Box`, unlike `UniqueBorrowed`, it also has `noalias` on returns. - UniqueOwned -} - -#[derive(Copy, Clone)] -pub struct PointeeInfo { - pub size: Size, - pub align: Align, - pub safe: Option, -} - -pub trait LayoutLlvmExt<'tcx> { - fn is_llvm_immediate(&self) -> bool; - fn is_llvm_scalar_pair<'a>(&self) -> bool; - fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type; - fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, offset: Size) -> Type; - fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - index: usize) -> Type; - fn llvm_field_index(&self, index: usize) -> u64; - fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) - -> Option; -} - -impl<'tcx> LayoutLlvmExt<'tcx> for TyLayout<'tcx> { - fn is_llvm_immediate(&self) -> bool { - match self.abi { - layout::Abi::Scalar(_) | - layout::Abi::Vector { .. } => true, - layout::Abi::ScalarPair(..) => false, - layout::Abi::Uninhabited | - layout::Abi::Aggregate { .. } => self.is_zst() - } - } - - fn is_llvm_scalar_pair<'a>(&self) -> bool { - match self.abi { - layout::Abi::ScalarPair(..) => true, - layout::Abi::Uninhabited | - layout::Abi::Scalar(_) | - layout::Abi::Vector { .. } | - layout::Abi::Aggregate { .. } => false - } - } - - /// Get the LLVM type corresponding to a Rust type, i.e. `rustc::ty::Ty`. - /// The pointee type of the pointer in `PlaceRef` is always this type. - /// For sized types, it is also the right LLVM type for an `alloca` - /// containing a value of that type, and most immediates (except `bool`). - /// Unsized types, however, are represented by a "minimal unit", e.g. - /// `[T]` becomes `T`, while `str` and `Trait` turn into `i8` - this - /// is useful for indexing slices, as `&[T]`'s data pointer is `T*`. - /// If the type is an unsized struct, the regular layout is generated, - /// with the inner-most trailing unsized field using the "minimal unit" - /// of that field's type - this is useful for taking the address of - /// that field and ensuring the struct has the right alignment. - fn llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { - // Use a different cache for scalars because pointers to DSTs - // can be either fat or thin (data pointers of fat pointers). - if let Some(&llty) = cx.scalar_lltypes.borrow().get(&self.ty) { - return llty; - } - let llty = match self.ty.sty { - ty::TyRef(_, ty, _) | - ty::TyRawPtr(ty::TypeAndMut { ty, .. }) => { - cx.layout_of(ty).llvm_type(cx).ptr_to() - } - ty::TyAdt(def, _) if def.is_box() => { - cx.layout_of(self.ty.boxed_ty()).llvm_type(cx).ptr_to() - } - ty::TyFnPtr(sig) => { - let sig = cx.tcx.normalize_erasing_late_bound_regions( - ty::ParamEnv::reveal_all(), - &sig, - ); - FnType::new(cx, sig, &[]).llvm_type(cx).ptr_to() - } - _ => self.scalar_llvm_type_at(cx, scalar, Size::from_bytes(0)) - }; - cx.scalar_lltypes.borrow_mut().insert(self.ty, llty); - return llty; - } - - - // Check the cache. - let variant_index = match self.variants { - layout::Variants::Single { index } => Some(index), - _ => None - }; - if let Some(&llty) = cx.lltypes.borrow().get(&(self.ty, variant_index)) { - return llty; - } - - debug!("llvm_type({:#?})", self); - - assert!(!self.ty.has_escaping_regions(), "{:?} has escaping regions", self.ty); - - // Make sure lifetimes are erased, to avoid generating distinct LLVM - // types for Rust types that only differ in the choice of lifetimes. - let normal_ty = cx.tcx.erase_regions(&self.ty); - - let mut defer = None; - let llty = if self.ty != normal_ty { - let mut layout = cx.layout_of(normal_ty); - if let Some(v) = variant_index { - layout = layout.for_variant(cx, v); - } - layout.llvm_type(cx) - } else { - uncached_llvm_type(cx, *self, &mut defer) - }; - debug!("--> mapped {:#?} to llty={:?}", self, llty); - - cx.lltypes.borrow_mut().insert((self.ty, variant_index), llty); - - if let Some((mut llty, layout)) = defer { - let (llfields, packed) = struct_llfields(cx, layout); - llty.set_struct_body(&llfields, packed) - } - - llty - } - - fn immediate_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>) -> Type { - if let layout::Abi::Scalar(ref scalar) = self.abi { - if scalar.is_bool() { - return Type::i1(cx); - } - } - self.llvm_type(cx) - } - - fn scalar_llvm_type_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - scalar: &layout::Scalar, offset: Size) -> Type { - match scalar.value { - layout::Int(i, _) => Type::from_integer(cx, i), - layout::F32 => Type::f32(cx), - layout::F64 => Type::f64(cx), - layout::Pointer => { - // If we know the alignment, pick something better than i8. - let pointee = if let Some(pointee) = self.pointee_info_at(cx, offset) { - Type::pointee_for_abi_align(cx, pointee.align) - } else { - Type::i8(cx) - }; - pointee.ptr_to() - } - } - } - - fn scalar_pair_element_llvm_type<'a>(&self, cx: &CodegenCx<'a, 'tcx>, - index: usize) -> Type { - // HACK(eddyb) special-case fat pointers until LLVM removes - // pointee types, to avoid bitcasting every `OperandRef::deref`. - match self.ty.sty { - ty::TyRef(..) | - ty::TyRawPtr(_) => { - return self.field(cx, index).llvm_type(cx); - } - ty::TyAdt(def, _) if def.is_box() => { - let ptr_ty = cx.tcx.mk_mut_ptr(self.ty.boxed_ty()); - return cx.layout_of(ptr_ty).scalar_pair_element_llvm_type(cx, index); - } - _ => {} - } - - let (a, b) = match self.abi { - layout::Abi::ScalarPair(ref a, ref b) => (a, b), - _ => bug!("TyLayout::scalar_pair_element_llty({:?}): not applicable", self) - }; - let scalar = [a, b][index]; - - // Make sure to return the same type `immediate_llvm_type` would, - // to avoid dealing with two types and the associated conversions. - // This means that `(bool, bool)` is represented as `{i1, i1}`, - // both in memory and as an immediate, while `bool` is typically - // `i8` in memory and only `i1` when immediate. While we need to - // load/store `bool` as `i8` to avoid crippling LLVM optimizations, - // `i1` in a LLVM aggregate is valid and mostly equivalent to `i8`. - if scalar.is_bool() { - return Type::i1(cx); - } - - let offset = if index == 0 { - Size::from_bytes(0) - } else { - a.value.size(cx).abi_align(b.value.align(cx)) - }; - self.scalar_llvm_type_at(cx, scalar, offset) - } - - fn llvm_field_index(&self, index: usize) -> u64 { - match self.abi { - layout::Abi::Scalar(_) | - layout::Abi::ScalarPair(..) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) - } - _ => {} - } - match self.fields { - layout::FieldPlacement::Union(_) => { - bug!("TyLayout::llvm_field_index({:?}): not applicable", self) - } - - layout::FieldPlacement::Array { .. } => { - index as u64 - } - - layout::FieldPlacement::Arbitrary { .. } => { - 1 + (self.fields.memory_index(index) as u64) * 2 - } - } - } - - fn pointee_info_at<'a>(&self, cx: &CodegenCx<'a, 'tcx>, offset: Size) - -> Option { - if let Some(&pointee) = cx.pointee_infos.borrow().get(&(self.ty, offset)) { - return pointee; - } - - let mut result = None; - match self.ty.sty { - ty::TyRawPtr(mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(mt.ty); - result = Some(PointeeInfo { - size, - align, - safe: None - }); - } - - ty::TyRef(_, ty, mt) if offset.bytes() == 0 => { - let (size, align) = cx.size_and_align_of(ty); - - let kind = match mt { - hir::MutImmutable => if cx.type_is_freeze(ty) { - PointerKind::Frozen - } else { - PointerKind::Shared - }, - hir::MutMutable => { - if cx.tcx.sess.opts.debugging_opts.mutable_noalias || - cx.tcx.sess.panic_strategy() == PanicStrategy::Abort { - PointerKind::UniqueBorrowed - } else { - PointerKind::Shared - } - } - }; - - result = Some(PointeeInfo { - size, - align, - safe: Some(kind) - }); - } - - _ => { - let mut data_variant = match self.variants { - layout::Variants::NicheFilling { dataful_variant, .. } => { - // Only the niche itself is always initialized, - // so only check for a pointer at its offset. - // - // If the niche is a pointer, it's either valid - // (according to its type), or null (which the - // niche field's scalar validity range encodes). - // This allows using `dereferenceable_or_null` - // for e.g. `Option<&T>`, and this will continue - // to work as long as we don't start using more - // niches than just null (e.g. the first page - // of the address space, or unaligned pointers). - if self.fields.offset(0) == offset { - Some(self.for_variant(cx, dataful_variant)) - } else { - None - } - } - _ => Some(*self) - }; - - if let Some(variant) = data_variant { - // We're not interested in any unions. - if let layout::FieldPlacement::Union(_) = variant.fields { - data_variant = None; - } - } - - if let Some(variant) = data_variant { - let ptr_end = offset + layout::Pointer.size(cx); - for i in 0..variant.fields.count() { - let field_start = variant.fields.offset(i); - if field_start <= offset { - let field = variant.field(cx, i); - if ptr_end <= field_start + field.size { - // We found the right field, look inside it. - result = field.pointee_info_at(cx, offset - field_start); - break; - } - } - } - } - - // FIXME(eddyb) This should be for `ptr::Unique`, not `Box`. - if let Some(ref mut pointee) = result { - if let ty::TyAdt(def, _) = self.ty.sty { - if def.is_box() && offset.bytes() == 0 { - pointee.safe = Some(PointerKind::UniqueOwned); - } - } - } - } - } - - cx.pointee_infos.borrow_mut().insert((self.ty, offset), result); - result - } -} diff --git a/src/librustc_trans/value.rs b/src/librustc_trans/value.rs deleted file mode 100644 index 287ad87caac..00000000000 --- a/src/librustc_trans/value.rs +++ /dev/null @@ -1,24 +0,0 @@ -// Copyright 2013 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use llvm; - -use std::fmt; - -#[derive(Copy, Clone, PartialEq)] -pub struct Value(pub llvm::ValueRef); - -impl fmt::Debug for Value { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.write_str(&llvm::build_string(|s| unsafe { - llvm::LLVMRustWriteValueToString(self.0, s); - }).expect("nun-UTF8 value description from LLVM")) - } -} diff --git a/src/librustc_trans_utils/Cargo.toml b/src/librustc_trans_utils/Cargo.toml deleted file mode 100644 index 323d9d1ceda..00000000000 --- a/src/librustc_trans_utils/Cargo.toml +++ /dev/null @@ -1,23 +0,0 @@ -[package] -authors = ["The Rust Project Developers"] -name = "rustc_trans_utils" -version = "0.0.0" - -[lib] -name = "rustc_trans_utils" -path = "lib.rs" -crate-type = ["dylib"] -test = false - -[dependencies] -ar = "0.3.0" -flate2 = "1.0" -log = "0.4" - -syntax = { path = "../libsyntax" } -syntax_pos = { path = "../libsyntax_pos" } -rustc = { path = "../librustc" } -rustc_target = { path = "../librustc_target" } -rustc_data_structures = { path = "../librustc_data_structures" } -rustc_mir = { path = "../librustc_mir" } -rustc_incremental = { path = "../librustc_incremental" } diff --git a/src/librustc_trans_utils/lib.rs b/src/librustc_trans_utils/lib.rs deleted file mode 100644 index b91f4e4dffb..00000000000 --- a/src/librustc_trans_utils/lib.rs +++ /dev/null @@ -1,63 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] - -#![feature(box_patterns)] -#![feature(box_syntax)] -#![feature(custom_attribute)] -#![allow(unused_attributes)] -#![feature(quote)] -#![feature(rustc_diagnostic_macros)] - -extern crate ar; -extern crate flate2; -#[macro_use] -extern crate log; - -#[macro_use] -extern crate rustc; -extern crate rustc_target; -extern crate rustc_mir; -extern crate rustc_incremental; -extern crate syntax; -extern crate syntax_pos; -#[macro_use] extern crate rustc_data_structures; - -pub extern crate rustc as __rustc; - -use rustc::ty::TyCtxt; - -pub mod link; -pub mod trans_crate; -pub mod symbol_names; -pub mod symbol_names_test; - -/// check for the #[rustc_error] annotation, which forces an -/// error in trans. This is used to write compile-fail tests -/// that actually test that compilation succeeds without -/// reporting an error. -pub fn check_for_rustc_errors_attr(tcx: TyCtxt) { - if let Some((id, span, _)) = *tcx.sess.entry_fn.borrow() { - let main_def_id = tcx.hir.local_def_id(id); - - if tcx.has_attr(main_def_id, "rustc_error") { - tcx.sess.span_fatal(span, "compilation successful"); - } - } -} - -__build_diagnostic_array! { librustc_trans_utils, DIAGNOSTICS } diff --git a/src/librustc_trans_utils/link.rs b/src/librustc_trans_utils/link.rs deleted file mode 100644 index aabe931d79c..00000000000 --- a/src/librustc_trans_utils/link.rs +++ /dev/null @@ -1,191 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use rustc::session::config::{self, OutputFilenames, Input, OutputType}; -use rustc::session::Session; -use rustc::middle::cstore::{self, LinkMeta}; -use rustc::hir::svh::Svh; -use std::path::{Path, PathBuf}; -use syntax::{ast, attr}; -use syntax_pos::Span; - -pub fn out_filename(sess: &Session, - crate_type: config::CrateType, - outputs: &OutputFilenames, - crate_name: &str) - -> PathBuf { - let default_filename = filename_for_input(sess, crate_type, crate_name, outputs); - let out_filename = outputs.outputs.get(&OutputType::Exe) - .and_then(|s| s.to_owned()) - .or_else(|| outputs.single_output_file.clone()) - .unwrap_or(default_filename); - - check_file_is_writeable(&out_filename, sess); - - out_filename -} - -// Make sure files are writeable. Mac, FreeBSD, and Windows system linkers -// check this already -- however, the Linux linker will happily overwrite a -// read-only file. We should be consistent. -pub fn check_file_is_writeable(file: &Path, sess: &Session) { - if !is_writeable(file) { - sess.fatal(&format!("output file {} is not writeable -- check its \ - permissions", file.display())); - } -} - -fn is_writeable(p: &Path) -> bool { - match p.metadata() { - Err(..) => true, - Ok(m) => !m.permissions().readonly() - } -} - -pub fn build_link_meta(crate_hash: Svh) -> LinkMeta { - let r = LinkMeta { - crate_hash, - }; - info!("{:?}", r); - return r; -} - -pub fn find_crate_name(sess: Option<&Session>, - attrs: &[ast::Attribute], - input: &Input) -> String { - let validate = |s: String, span: Option| { - cstore::validate_crate_name(sess, &s, span); - s - }; - - // Look in attributes 100% of the time to make sure the attribute is marked - // as used. After doing this, however, we still prioritize a crate name from - // the command line over one found in the #[crate_name] attribute. If we - // find both we ensure that they're the same later on as well. - let attr_crate_name = attr::find_by_name(attrs, "crate_name") - .and_then(|at| at.value_str().map(|s| (at, s))); - - if let Some(sess) = sess { - if let Some(ref s) = sess.opts.crate_name { - if let Some((attr, name)) = attr_crate_name { - if name != &**s { - let msg = format!("--crate-name and #[crate_name] are \ - required to match, but `{}` != `{}`", - s, name); - sess.span_err(attr.span, &msg); - } - } - return validate(s.clone(), None); - } - } - - if let Some((attr, s)) = attr_crate_name { - return validate(s.to_string(), Some(attr.span)); - } - if let Input::File(ref path) = *input { - if let Some(s) = path.file_stem().and_then(|s| s.to_str()) { - if s.starts_with("-") { - let msg = format!("crate names cannot start with a `-`, but \ - `{}` has a leading hyphen", s); - if let Some(sess) = sess { - sess.err(&msg); - } - } else { - return validate(s.replace("-", "_"), None); - } - } - } - - "rust_out".to_string() -} - -pub fn filename_for_input(sess: &Session, - crate_type: config::CrateType, - crate_name: &str, - outputs: &OutputFilenames) -> PathBuf { - let libname = format!("{}{}", crate_name, sess.opts.cg.extra_filename); - - match crate_type { - config::CrateTypeRlib => { - outputs.out_directory.join(&format!("lib{}.rlib", libname)) - } - config::CrateTypeCdylib | - config::CrateTypeProcMacro | - config::CrateTypeDylib => { - let (prefix, suffix) = (&sess.target.target.options.dll_prefix, - &sess.target.target.options.dll_suffix); - outputs.out_directory.join(&format!("{}{}{}", prefix, libname, - suffix)) - } - config::CrateTypeStaticlib => { - let (prefix, suffix) = (&sess.target.target.options.staticlib_prefix, - &sess.target.target.options.staticlib_suffix); - outputs.out_directory.join(&format!("{}{}{}", prefix, libname, - suffix)) - } - config::CrateTypeExecutable => { - let suffix = &sess.target.target.options.exe_suffix; - let out_filename = outputs.path(OutputType::Exe); - if suffix.is_empty() { - out_filename.to_path_buf() - } else { - out_filename.with_extension(&suffix[1..]) - } - } - } -} - -/// Returns default crate type for target -/// -/// Default crate type is used when crate type isn't provided neither -/// through cmd line arguments nor through crate attributes -/// -/// It is CrateTypeExecutable for all platforms but iOS as there is no -/// way to run iOS binaries anyway without jailbreaking and -/// interaction with Rust code through static library is the only -/// option for now -pub fn default_output_for_target(sess: &Session) -> config::CrateType { - if !sess.target.target.options.executables { - config::CrateTypeStaticlib - } else { - config::CrateTypeExecutable - } -} - -/// Checks if target supports crate_type as output -pub fn invalid_output_for_target(sess: &Session, - crate_type: config::CrateType) -> bool { - match crate_type { - config::CrateTypeCdylib | - config::CrateTypeDylib | - config::CrateTypeProcMacro => { - if !sess.target.target.options.dynamic_linking { - return true - } - if sess.crt_static() && !sess.target.target.options.crt_static_allows_dylibs { - return true - } - } - _ => {} - } - if sess.target.target.options.only_cdylib { - match crate_type { - config::CrateTypeProcMacro | config::CrateTypeDylib => return true, - _ => {} - } - } - if !sess.target.target.options.executables { - if crate_type == config::CrateTypeExecutable { - return true - } - } - - false -} diff --git a/src/librustc_trans_utils/symbol_names.rs b/src/librustc_trans_utils/symbol_names.rs deleted file mode 100644 index be5bff60805..00000000000 --- a/src/librustc_trans_utils/symbol_names.rs +++ /dev/null @@ -1,435 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Rust Linkage Model and Symbol Names -//! ======================================= -//! -//! The semantic model of Rust linkage is, broadly, that "there's no global -//! namespace" between crates. Our aim is to preserve the illusion of this -//! model despite the fact that it's not *quite* possible to implement on -//! modern linkers. We initially didn't use system linkers at all, but have -//! been convinced of their utility. -//! -//! There are a few issues to handle: -//! -//! - Linkers operate on a flat namespace, so we have to flatten names. -//! We do this using the C++ namespace-mangling technique. Foo::bar -//! symbols and such. -//! -//! - Symbols for distinct items with the same *name* need to get different -//! linkage-names. Examples of this are monomorphizations of functions or -//! items within anonymous scopes that end up having the same path. -//! -//! - Symbols in different crates but with same names "within" the crate need -//! to get different linkage-names. -//! -//! - Symbol names should be deterministic: Two consecutive runs of the -//! compiler over the same code base should produce the same symbol names for -//! the same items. -//! -//! - Symbol names should not depend on any global properties of the code base, -//! so that small modifications to the code base do not result in all symbols -//! changing. In previous versions of the compiler, symbol names incorporated -//! the SVH (Stable Version Hash) of the crate. This scheme turned out to be -//! infeasible when used in conjunction with incremental compilation because -//! small code changes would invalidate all symbols generated previously. -//! -//! - Even symbols from different versions of the same crate should be able to -//! live next to each other without conflict. -//! -//! In order to fulfill the above requirements the following scheme is used by -//! the compiler: -//! -//! The main tool for avoiding naming conflicts is the incorporation of a 64-bit -//! hash value into every exported symbol name. Anything that makes a difference -//! to the symbol being named, but does not show up in the regular path needs to -//! be fed into this hash: -//! -//! - Different monomorphizations of the same item have the same path but differ -//! in their concrete type parameters, so these parameters are part of the -//! data being digested for the symbol hash. -//! -//! - Rust allows items to be defined in anonymous scopes, such as in -//! `fn foo() { { fn bar() {} } { fn bar() {} } }`. Both `bar` functions have -//! the path `foo::bar`, since the anonymous scopes do not contribute to the -//! path of an item. The compiler already handles this case via so-called -//! disambiguating `DefPaths` which use indices to distinguish items with the -//! same name. The DefPaths of the functions above are thus `foo[0]::bar[0]` -//! and `foo[0]::bar[1]`. In order to incorporate this disambiguation -//! information into the symbol name too, these indices are fed into the -//! symbol hash, so that the above two symbols would end up with different -//! hash values. -//! -//! The two measures described above suffice to avoid intra-crate conflicts. In -//! order to also avoid inter-crate conflicts two more measures are taken: -//! -//! - The name of the crate containing the symbol is prepended to the symbol -//! name, i.e. symbols are "crate qualified". For example, a function `foo` in -//! module `bar` in crate `baz` would get a symbol name like -//! `baz::bar::foo::{hash}` instead of just `bar::foo::{hash}`. This avoids -//! simple conflicts between functions from different crates. -//! -//! - In order to be able to also use symbols from two versions of the same -//! crate (which naturally also have the same name), a stronger measure is -//! required: The compiler accepts an arbitrary "disambiguator" value via the -//! `-C metadata` commandline argument. This disambiguator is then fed into -//! the symbol hash of every exported item. Consequently, the symbols in two -//! identical crates but with different disambiguators are not in conflict -//! with each other. This facility is mainly intended to be used by build -//! tools like Cargo. -//! -//! A note on symbol name stability -//! ------------------------------- -//! Previous versions of the compiler resorted to feeding NodeIds into the -//! symbol hash in order to disambiguate between items with the same path. The -//! current version of the name generation algorithm takes great care not to do -//! that, since NodeIds are notoriously unstable: A small change to the -//! code base will offset all NodeIds after the change and thus, much as using -//! the SVH in the hash, invalidate an unbounded number of symbol names. This -//! makes re-using previously compiled code for incremental compilation -//! virtually impossible. Thus, symbol hash generation exclusively relies on -//! DefPaths which are much more robust in the face of changes to the code base. - -use rustc::middle::weak_lang_items; -use rustc_mir::monomorphize::Instance; -use rustc_mir::monomorphize::item::{MonoItem, MonoItemExt, InstantiationMode}; -use rustc::hir::def_id::{DefId, LOCAL_CRATE}; -use rustc::hir::map as hir_map; -use rustc::ty::{self, Ty, TyCtxt, TypeFoldable}; -use rustc::ty::fold::TypeVisitor; -use rustc::ty::item_path::{self, ItemPathBuffer, RootMode}; -use rustc::ty::maps::Providers; -use rustc::ty::subst::Substs; -use rustc::hir::map::definitions::DefPathData; -use rustc::util::common::record_time; - -use syntax::attr; -use syntax_pos::symbol::Symbol; - -use std::fmt::Write; - -pub fn provide(providers: &mut Providers) { - *providers = Providers { - def_symbol_name, - symbol_name, - - ..*providers - }; -} - -fn get_symbol_hash<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, - - // the DefId of the item this name is for - def_id: DefId, - - // instance this name will be for - instance: Instance<'tcx>, - - // type of the item, without any generic - // parameters substituted; this is - // included in the hash as a kind of - // safeguard. - item_type: Ty<'tcx>, - - // values for generic type parameters, - // if any. - substs: &'tcx Substs<'tcx>) - -> u64 { - debug!("get_symbol_hash(def_id={:?}, parameters={:?})", def_id, substs); - - let mut hasher = ty::util::TypeIdHasher::::new(tcx); - - record_time(&tcx.sess.perf_stats.symbol_hash_time, || { - // the main symbol name is not necessarily unique; hash in the - // compiler's internal def-path, guaranteeing each symbol has a - // truly unique path - hasher.hash(tcx.def_path_hash(def_id)); - - // Include the main item-type. Note that, in this case, the - // assertions about `needs_subst` may not hold, but this item-type - // ought to be the same for every reference anyway. - assert!(!item_type.has_erasable_regions()); - hasher.visit_ty(item_type); - - // If this is a function, we hash the signature as well. - // This is not *strictly* needed, but it may help in some - // situations, see the `run-make/a-b-a-linker-guard` test. - if let ty::TyFnDef(..) = item_type.sty { - item_type.fn_sig(tcx).visit_with(&mut hasher); - } - - // also include any type parameters (for generic items) - assert!(!substs.has_erasable_regions()); - assert!(!substs.needs_subst()); - substs.visit_with(&mut hasher); - - let is_generic = substs.types().next().is_some(); - let avoid_cross_crate_conflicts = - // If this is an instance of a generic function, we also hash in - // the ID of the instantiating crate. This avoids symbol conflicts - // in case the same instances is emitted in two crates of the same - // project. - is_generic || - - // If we're dealing with an instance of a function that's inlined from - // another crate but we're marking it as globally shared to our - // compliation (aka we're not making an internal copy in each of our - // codegen units) then this symbol may become an exported (but hidden - // visibility) symbol. This means that multiple crates may do the same - // and we want to be sure to avoid any symbol conflicts here. - match MonoItem::Fn(instance).instantiation_mode(tcx) { - InstantiationMode::GloballyShared { may_conflict: true } => true, - _ => false, - }; - - if avoid_cross_crate_conflicts { - let instantiating_crate = if is_generic { - if !def_id.is_local() && tcx.share_generics() { - // If we are re-using a monomorphization from another crate, - // we have to compute the symbol hash accordingly. - let upstream_monomorphizations = - tcx.upstream_monomorphizations_for(def_id); - - upstream_monomorphizations.and_then(|monos| monos.get(&substs) - .cloned()) - .unwrap_or(LOCAL_CRATE) - } else { - LOCAL_CRATE - } - } else { - LOCAL_CRATE - }; - - hasher.hash(&tcx.original_crate_name(instantiating_crate).as_str()[..]); - hasher.hash(&tcx.crate_disambiguator(instantiating_crate)); - } - }); - - // 64 bits should be enough to avoid collisions. - hasher.finish() -} - -fn def_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId) - -> ty::SymbolName -{ - let mut buffer = SymbolPathBuffer::new(); - item_path::with_forced_absolute_paths(|| { - tcx.push_item_path(&mut buffer, def_id); - }); - buffer.into_interned() -} - -fn symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> ty::SymbolName -{ - ty::SymbolName { name: Symbol::intern(&compute_symbol_name(tcx, instance)).as_interned_str() } -} - -fn compute_symbol_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, instance: Instance<'tcx>) - -> String -{ - let def_id = instance.def_id(); - let substs = instance.substs; - - debug!("symbol_name(def_id={:?}, substs={:?})", - def_id, substs); - - let node_id = tcx.hir.as_local_node_id(def_id); - - if let Some(id) = node_id { - if *tcx.sess.plugin_registrar_fn.get() == Some(id) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_plugin_registrar_symbol(disambiguator); - } - if *tcx.sess.derive_registrar_fn.get() == Some(id) { - let disambiguator = tcx.sess.local_crate_disambiguator(); - return tcx.sess.generate_derive_registrar_symbol(disambiguator); - } - } - - // FIXME(eddyb) Precompute a custom symbol name based on attributes. - let attrs = tcx.get_attrs(def_id); - let is_foreign = if let Some(id) = node_id { - match tcx.hir.get(id) { - hir_map::NodeForeignItem(_) => true, - _ => false - } - } else { - tcx.is_foreign_item(def_id) - }; - - if let Some(name) = weak_lang_items::link_name(&attrs) { - return name.to_string(); - } - - if is_foreign { - if let Some(name) = attr::first_attr_value_str_by_name(&attrs, "link_name") { - return name.to_string(); - } - // Don't mangle foreign items. - return tcx.item_name(def_id).to_string(); - } - - if let Some(name) = tcx.trans_fn_attrs(def_id).export_name { - // Use provided name - return name.to_string(); - } - - if attr::contains_name(&attrs, "no_mangle") { - // Don't mangle - return tcx.item_name(def_id).to_string(); - } - - // We want to compute the "type" of this item. Unfortunately, some - // kinds of items (e.g., closures) don't have an entry in the - // item-type array. So walk back up the find the closest parent - // that DOES have an entry. - let mut ty_def_id = def_id; - let instance_ty; - loop { - let key = tcx.def_key(ty_def_id); - match key.disambiguated_data.data { - DefPathData::TypeNs(_) | - DefPathData::ValueNs(_) => { - instance_ty = tcx.type_of(ty_def_id); - break; - } - _ => { - // if we're making a symbol for something, there ought - // to be a value or type-def or something in there - // *somewhere* - ty_def_id.index = key.parent.unwrap_or_else(|| { - bug!("finding type for {:?}, encountered def-id {:?} with no \ - parent", def_id, ty_def_id); - }); - } - } - } - - // Erase regions because they may not be deterministic when hashed - // and should not matter anyhow. - let instance_ty = tcx.erase_regions(&instance_ty); - - let hash = get_symbol_hash(tcx, def_id, instance, instance_ty, substs); - - SymbolPathBuffer::from_interned(tcx.def_symbol_name(def_id)).finish(hash) -} - -// Follow C++ namespace-mangling style, see -// http://en.wikipedia.org/wiki/Name_mangling for more info. -// -// It turns out that on macOS you can actually have arbitrary symbols in -// function names (at least when given to LLVM), but this is not possible -// when using unix's linker. Perhaps one day when we just use a linker from LLVM -// we won't need to do this name mangling. The problem with name mangling is -// that it seriously limits the available characters. For example we can't -// have things like &T in symbol names when one would theoretically -// want them for things like impls of traits on that type. -// -// To be able to work on all platforms and get *some* reasonable output, we -// use C++ name-mangling. -struct SymbolPathBuffer { - result: String, - temp_buf: String -} - -impl SymbolPathBuffer { - fn new() -> Self { - let mut result = SymbolPathBuffer { - result: String::with_capacity(64), - temp_buf: String::with_capacity(16) - }; - result.result.push_str("_ZN"); // _Z == Begin name-sequence, N == nested - result - } - - fn from_interned(symbol: ty::SymbolName) -> Self { - let mut result = SymbolPathBuffer { - result: String::with_capacity(64), - temp_buf: String::with_capacity(16) - }; - result.result.push_str(&symbol.name.as_str()); - result - } - - fn into_interned(self) -> ty::SymbolName { - ty::SymbolName { name: Symbol::intern(&self.result).as_interned_str() } - } - - fn finish(mut self, hash: u64) -> String { - // E = end name-sequence - let _ = write!(self.result, "17h{:016x}E", hash); - self.result - } -} - -impl ItemPathBuffer for SymbolPathBuffer { - fn root_mode(&self) -> &RootMode { - const ABSOLUTE: &'static RootMode = &RootMode::Absolute; - ABSOLUTE - } - - fn push(&mut self, text: &str) { - self.temp_buf.clear(); - let need_underscore = sanitize(&mut self.temp_buf, text); - let _ = write!(self.result, "{}", self.temp_buf.len() + (need_underscore as usize)); - if need_underscore { - self.result.push('_'); - } - self.result.push_str(&self.temp_buf); - } -} - -// Name sanitation. LLVM will happily accept identifiers with weird names, but -// gas doesn't! -// gas accepts the following characters in symbols: a-z, A-Z, 0-9, ., _, $ -// -// returns true if an underscore must be added at the start -pub fn sanitize(result: &mut String, s: &str) -> bool { - for c in s.chars() { - match c { - // Escape these with $ sequences - '@' => result.push_str("$SP$"), - '*' => result.push_str("$BP$"), - '&' => result.push_str("$RF$"), - '<' => result.push_str("$LT$"), - '>' => result.push_str("$GT$"), - '(' => result.push_str("$LP$"), - ')' => result.push_str("$RP$"), - ',' => result.push_str("$C$"), - - // '.' doesn't occur in types and functions, so reuse it - // for ':' and '-' - '-' | ':' => result.push('.'), - - // These are legal symbols - 'a' ... 'z' - | 'A' ... 'Z' - | '0' ... '9' - | '_' | '.' | '$' => result.push(c), - - _ => { - result.push('$'); - for c in c.escape_unicode().skip(1) { - match c { - '{' => {}, - '}' => result.push('$'), - c => result.push(c), - } - } - } - } - } - - // Underscore-qualify anything that didn't start as an ident. - !result.is_empty() && - result.as_bytes()[0] != '_' as u8 && - ! (result.as_bytes()[0] as char).is_xid_start() -} diff --git a/src/librustc_trans_utils/symbol_names_test.rs b/src/librustc_trans_utils/symbol_names_test.rs deleted file mode 100644 index 47bbd67fb5c..00000000000 --- a/src/librustc_trans_utils/symbol_names_test.rs +++ /dev/null @@ -1,79 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! Walks the crate looking for items/impl-items/trait-items that have -//! either a `rustc_symbol_name` or `rustc_item_path` attribute and -//! generates an error giving, respectively, the symbol name or -//! item-path. This is used for unit testing the code that generates -//! paths etc in all kinds of annoying scenarios. - -use rustc::hir; -use rustc::ty::TyCtxt; -use syntax::ast; - -use rustc_mir::monomorphize::Instance; - -const SYMBOL_NAME: &'static str = "rustc_symbol_name"; -const ITEM_PATH: &'static str = "rustc_item_path"; - -pub fn report_symbol_names<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>) { - // if the `rustc_attrs` feature is not enabled, then the - // attributes we are interested in cannot be present anyway, so - // skip the walk. - if !tcx.features().rustc_attrs { - return; - } - - tcx.dep_graph.with_ignore(|| { - let mut visitor = SymbolNamesTest { tcx: tcx }; - tcx.hir.krate().visit_all_item_likes(&mut visitor); - }) -} - -struct SymbolNamesTest<'a, 'tcx:'a> { - tcx: TyCtxt<'a, 'tcx, 'tcx>, -} - -impl<'a, 'tcx> SymbolNamesTest<'a, 'tcx> { - fn process_attrs(&mut self, - node_id: ast::NodeId) { - let tcx = self.tcx; - let def_id = tcx.hir.local_def_id(node_id); - for attr in tcx.get_attrs(def_id).iter() { - if attr.check_name(SYMBOL_NAME) { - // for now, can only use on monomorphic names - let instance = Instance::mono(tcx, def_id); - let name = self.tcx.symbol_name(instance); - tcx.sess.span_err(attr.span, &format!("symbol-name({})", name)); - } else if attr.check_name(ITEM_PATH) { - let path = tcx.item_path_str(def_id); - tcx.sess.span_err(attr.span, &format!("item-path({})", path)); - } - - // (*) The formatting of `tag({})` is chosen so that tests can elect - // to test the entirety of the string, if they choose, or else just - // some subset. - } - } -} - -impl<'a, 'tcx> hir::itemlikevisit::ItemLikeVisitor<'tcx> for SymbolNamesTest<'a, 'tcx> { - fn visit_item(&mut self, item: &'tcx hir::Item) { - self.process_attrs(item.id); - } - - fn visit_trait_item(&mut self, trait_item: &'tcx hir::TraitItem) { - self.process_attrs(trait_item.id); - } - - fn visit_impl_item(&mut self, impl_item: &'tcx hir::ImplItem) { - self.process_attrs(impl_item.id); - } -} diff --git a/src/librustc_trans_utils/trans_crate.rs b/src/librustc_trans_utils/trans_crate.rs deleted file mode 100644 index 7b585d1060f..00000000000 --- a/src/librustc_trans_utils/trans_crate.rs +++ /dev/null @@ -1,296 +0,0 @@ -// Copyright 2014-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! The Rust compiler. -//! -//! # Note -//! -//! This API is completely unstable and subject to change. - -#![doc(html_logo_url = "https://www.rust-lang.org/logos/rust-logo-128x128-blk-v2.png", - html_favicon_url = "https://doc.rust-lang.org/favicon.ico", - html_root_url = "https://doc.rust-lang.org/nightly/")] -#![deny(warnings)] - -#![feature(box_syntax)] - -use std::any::Any; -use std::io::prelude::*; -use std::io::{self, Cursor}; -use std::fs::File; -use std::path::Path; -use std::sync::mpsc; - -use rustc_data_structures::owning_ref::OwningRef; -use rustc_data_structures::sync::Lrc; -use ar::{Archive, Builder, Header}; -use flate2::Compression; -use flate2::write::DeflateEncoder; - -use syntax::symbol::Symbol; -use rustc::hir::def_id::LOCAL_CRATE; -use rustc::session::{Session, CompileIncomplete}; -use rustc::session::config::{CrateType, OutputFilenames, PrintRequest}; -use rustc::ty::TyCtxt; -use rustc::ty::maps::Providers; -use rustc::middle::cstore::EncodedMetadata; -use rustc::middle::cstore::MetadataLoader; -use rustc::dep_graph::DepGraph; -use rustc_target::spec::Target; -use rustc_data_structures::fx::FxHashMap; -use rustc_mir::monomorphize::collector; -use link::{build_link_meta, out_filename}; - -pub use rustc_data_structures::sync::MetadataRef; - -pub trait TransCrate { - fn init(&self, _sess: &Session) {} - fn print(&self, _req: PrintRequest, _sess: &Session) {} - fn target_features(&self, _sess: &Session) -> Vec { vec![] } - fn print_passes(&self) {} - fn print_version(&self) {} - fn diagnostics(&self) -> &[(&'static str, &'static str)] { &[] } - - fn metadata_loader(&self) -> Box; - fn provide(&self, _providers: &mut Providers); - fn provide_extern(&self, _providers: &mut Providers); - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - rx: mpsc::Receiver> - ) -> Box; - - /// This is called on the returned `Box` from `trans_crate` - /// - /// # Panics - /// - /// Panics when the passed `Box` was not returned by `trans_crate`. - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete>; -} - -pub struct DummyTransCrate; - -impl TransCrate for DummyTransCrate { - fn metadata_loader(&self) -> Box { - box DummyMetadataLoader(()) - } - - fn provide(&self, _providers: &mut Providers) { - bug!("DummyTransCrate::provide"); - } - - fn provide_extern(&self, _providers: &mut Providers) { - bug!("DummyTransCrate::provide_extern"); - } - - fn trans_crate<'a, 'tcx>( - &self, - _tcx: TyCtxt<'a, 'tcx, 'tcx>, - _rx: mpsc::Receiver> - ) -> Box { - bug!("DummyTransCrate::trans_crate"); - } - - fn join_trans_and_link( - &self, - _trans: Box, - _sess: &Session, - _dep_graph: &DepGraph, - _outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete> { - bug!("DummyTransCrate::join_trans_and_link"); - } -} - -pub struct DummyMetadataLoader(()); - -impl MetadataLoader for DummyMetadataLoader { - fn get_rlib_metadata( - &self, - _target: &Target, - _filename: &Path - ) -> Result { - bug!("DummyMetadataLoader::get_rlib_metadata"); - } - - fn get_dylib_metadata( - &self, - _target: &Target, - _filename: &Path - ) -> Result { - bug!("DummyMetadataLoader::get_dylib_metadata"); - } -} - -pub struct NoLlvmMetadataLoader; - -impl MetadataLoader for NoLlvmMetadataLoader { - fn get_rlib_metadata(&self, _: &Target, filename: &Path) -> Result { - let file = File::open(filename) - .map_err(|e| format!("metadata file open err: {:?}", e))?; - let mut archive = Archive::new(file); - - while let Some(entry_result) = archive.next_entry() { - let mut entry = entry_result - .map_err(|e| format!("metadata section read err: {:?}", e))?; - if entry.header().identifier() == "rust.metadata.bin" { - let mut buf = Vec::new(); - io::copy(&mut entry, &mut buf).unwrap(); - let buf: OwningRef, [u8]> = OwningRef::new(buf).into(); - return Ok(rustc_erase_owner!(buf.map_owner_box())); - } - } - - Err("Couldn't find metadata section".to_string()) - } - - fn get_dylib_metadata( - &self, - _target: &Target, - _filename: &Path, - ) -> Result { - // FIXME: Support reading dylibs from llvm enabled rustc - self.get_rlib_metadata(_target, _filename) - } -} - -pub struct MetadataOnlyTransCrate(()); -pub struct OngoingCrateTranslation { - metadata: EncodedMetadata, - metadata_version: Vec, - crate_name: Symbol, -} - -impl MetadataOnlyTransCrate { - pub fn new() -> Box { - box MetadataOnlyTransCrate(()) - } -} - -impl TransCrate for MetadataOnlyTransCrate { - fn init(&self, sess: &Session) { - for cty in sess.opts.crate_types.iter() { - match *cty { - CrateType::CrateTypeRlib | CrateType::CrateTypeDylib | - CrateType::CrateTypeExecutable => {}, - _ => { - sess.parse_sess.span_diagnostic.warn( - &format!("LLVM unsupported, so output type {} is not supported", cty) - ); - }, - } - } - } - - fn metadata_loader(&self) -> Box { - box NoLlvmMetadataLoader - } - - fn provide(&self, providers: &mut Providers) { - ::symbol_names::provide(providers); - - providers.target_features_whitelist = |_tcx, _cnum| { - Lrc::new(FxHashMap()) // Just a dummy - }; - } - fn provide_extern(&self, _providers: &mut Providers) {} - - fn trans_crate<'a, 'tcx>( - &self, - tcx: TyCtxt<'a, 'tcx, 'tcx>, - _rx: mpsc::Receiver> - ) -> Box { - use rustc_mir::monomorphize::item::MonoItem; - - ::check_for_rustc_errors_attr(tcx); - ::symbol_names_test::report_symbol_names(tcx); - ::rustc_incremental::assert_dep_graph(tcx); - ::rustc_incremental::assert_module_sources::assert_module_sources(tcx); - ::rustc_mir::monomorphize::assert_symbols_are_distinct(tcx, - collector::collect_crate_mono_items( - tcx, - collector::MonoItemCollectionMode::Eager - ).0.iter() - ); - ::rustc::middle::dependency_format::calculate(tcx); - let _ = tcx.link_args(LOCAL_CRATE); - let _ = tcx.native_libraries(LOCAL_CRATE); - for trans_item in - collector::collect_crate_mono_items( - tcx, - collector::MonoItemCollectionMode::Eager - ).0 { - match trans_item { - MonoItem::Fn(inst) => { - let def_id = inst.def_id(); - if def_id.is_local() { - let _ = inst.def.is_inline(tcx); - let _ = tcx.trans_fn_attrs(def_id); - } - } - _ => {} - } - } - tcx.sess.abort_if_errors(); - - let link_meta = build_link_meta(tcx.crate_hash(LOCAL_CRATE)); - let metadata = tcx.encode_metadata(&link_meta); - - box OngoingCrateTranslation { - metadata: metadata, - metadata_version: tcx.metadata_encoding_version().to_vec(), - crate_name: tcx.crate_name(LOCAL_CRATE), - } - } - - fn join_trans_and_link( - &self, - trans: Box, - sess: &Session, - _dep_graph: &DepGraph, - outputs: &OutputFilenames, - ) -> Result<(), CompileIncomplete> { - let trans = trans.downcast::() - .expect("Expected MetadataOnlyTransCrate's OngoingCrateTranslation, found Box"); - for &crate_type in sess.opts.crate_types.iter() { - if crate_type != CrateType::CrateTypeRlib && crate_type != CrateType::CrateTypeDylib { - continue; - } - let output_name = - out_filename(sess, crate_type, &outputs, &trans.crate_name.as_str()); - let mut compressed = trans.metadata_version.clone(); - let metadata = if crate_type == CrateType::CrateTypeDylib { - DeflateEncoder::new(&mut compressed, Compression::fast()) - .write_all(&trans.metadata.raw_data) - .unwrap(); - &compressed - } else { - &trans.metadata.raw_data - }; - let mut builder = Builder::new(File::create(&output_name).unwrap()); - let header = Header::new("rust.metadata.bin".to_string(), metadata.len() as u64); - builder.append(&header, Cursor::new(metadata)).unwrap(); - } - - sess.abort_if_errors(); - if !sess.opts.crate_types.contains(&CrateType::CrateTypeRlib) - && !sess.opts.crate_types.contains(&CrateType::CrateTypeDylib) - { - sess.fatal("Executables are not supported by the metadata-only backend."); - } - Ok(()) - } -} diff --git a/src/librustc_typeck/check/intrinsic.rs b/src/librustc_typeck/check/intrinsic.rs index 97e4a35ea47..feb26e76162 100644 --- a/src/librustc_typeck/check/intrinsic.rs +++ b/src/librustc_typeck/check/intrinsic.rs @@ -72,7 +72,7 @@ fn equate_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, require_same_types(tcx, &cause, tcx.mk_fn_ptr(tcx.fn_sig(def_id)), fty); } -/// Remember to add all intrinsics here, in librustc_trans/trans/intrinsic.rs, +/// Remember to add all intrinsics here, in librustc_codegen_llvm/intrinsic.rs, /// and in libcore/intrinsics.rs pub fn check_intrinsic_type<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, it: &hir::ForeignItem) { diff --git a/src/librustc_typeck/check/op.rs b/src/librustc_typeck/check/op.rs index 23081b87d26..2dd22058d76 100644 --- a/src/librustc_typeck/check/op.rs +++ b/src/librustc_typeck/check/op.rs @@ -665,7 +665,7 @@ enum Op { /// `PartialEq` is not applicable. /// /// Reason #2 is the killer. I tried for a while to always use -/// overloaded logic and just check the types in constants/trans after +/// overloaded logic and just check the types in constants/codegen after /// the fact, and it worked fine, except for SIMD types. -nmatsakis fn is_builtin_binop(lhs: Ty, rhs: Ty, op: hir::BinOp) -> bool { match BinOpCategory::from(op) { diff --git a/src/librustc_typeck/coherence/builtin.rs b/src/librustc_typeck/coherence/builtin.rs index 2f08a54e10f..735ebbfcb3d 100644 --- a/src/librustc_typeck/coherence/builtin.rs +++ b/src/librustc_typeck/coherence/builtin.rs @@ -9,7 +9,7 @@ // except according to those terms. //! Check properties that are required by built-in traits and set -//! up data structures required by type-checking/translation. +//! up data structures required by type-checking/codegen. use rustc::infer::outlives::env::OutlivesEnvironment; use rustc::middle::region; diff --git a/src/librustc_typeck/collect.rs b/src/librustc_typeck/collect.rs index c2dde2d2e01..157f3ab76ca 100644 --- a/src/librustc_typeck/collect.rs +++ b/src/librustc_typeck/collect.rs @@ -48,7 +48,7 @@ use syntax::symbol::{Symbol, keywords}; use syntax::feature_gate; use syntax_pos::{Span, DUMMY_SP}; -use rustc::hir::{self, map as hir_map, TransFnAttrs, TransFnAttrFlags, Unsafety}; +use rustc::hir::{self, map as hir_map, CodegenFnAttrs, CodegenFnAttrFlags, Unsafety}; use rustc::hir::intravisit::{self, Visitor, NestedVisitorMap}; use rustc::hir::def::{Def, CtorKind}; use rustc::hir::def_id::{DefId, LOCAL_CRATE}; @@ -75,7 +75,7 @@ pub fn provide(providers: &mut Providers) { impl_trait_ref, impl_polarity, is_foreign_item, - trans_fn_attrs, + codegen_fn_attrs, ..*providers }; } @@ -1800,33 +1800,33 @@ fn linkage_by_name<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, def_id: DefId, name: & } } -fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAttrs { +fn codegen_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> CodegenFnAttrs { let attrs = tcx.get_attrs(id); - let mut trans_fn_attrs = TransFnAttrs::new(); + let mut codegen_fn_attrs = CodegenFnAttrs::new(); let whitelist = tcx.target_features_whitelist(LOCAL_CRATE); let mut inline_span = None; for attr in attrs.iter() { if attr.check_name("cold") { - trans_fn_attrs.flags |= TransFnAttrFlags::COLD; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::COLD; } else if attr.check_name("allocator") { - trans_fn_attrs.flags |= TransFnAttrFlags::ALLOCATOR; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::ALLOCATOR; } else if attr.check_name("unwind") { - trans_fn_attrs.flags |= TransFnAttrFlags::UNWIND; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::UNWIND; } else if attr.check_name("rustc_allocator_nounwind") { - trans_fn_attrs.flags |= TransFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND; } else if attr.check_name("naked") { - trans_fn_attrs.flags |= TransFnAttrFlags::NAKED; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NAKED; } else if attr.check_name("no_mangle") { - trans_fn_attrs.flags |= TransFnAttrFlags::NO_MANGLE; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_MANGLE; } else if attr.check_name("rustc_std_internal_symbol") { - trans_fn_attrs.flags |= TransFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::RUSTC_STD_INTERNAL_SYMBOL; } else if attr.check_name("no_debug") { - trans_fn_attrs.flags |= TransFnAttrFlags::NO_DEBUG; + codegen_fn_attrs.flags |= CodegenFnAttrFlags::NO_DEBUG; } else if attr.check_name("inline") { - trans_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { + codegen_fn_attrs.inline = attrs.iter().fold(InlineAttr::None, |ia, attr| { if attr.path != "inline" { return ia; } @@ -1862,7 +1862,7 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt }); } else if attr.check_name("export_name") { if let s @ Some(_) = attr.value_str() { - trans_fn_attrs.export_name = s; + codegen_fn_attrs.export_name = s; } else { struct_span_err!(tcx.sess, attr.span, E0558, "export_name attribute has invalid format") @@ -1875,10 +1875,10 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt `unsafe` function"; tcx.sess.span_err(attr.span, msg); } - from_target_feature(tcx, id, attr, &whitelist, &mut trans_fn_attrs.target_features); + from_target_feature(tcx, id, attr, &whitelist, &mut codegen_fn_attrs.target_features); } else if attr.check_name("linkage") { if let Some(val) = attr.value_str() { - trans_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); + codegen_fn_attrs.linkage = Some(linkage_by_name(tcx, id, &val.as_str())); } } } @@ -1887,8 +1887,8 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt // purpose functions as they wouldn't have the right target features // enabled. For that reason we also forbid #[inline(always)] as it can't be // respected. - if trans_fn_attrs.target_features.len() > 0 { - if trans_fn_attrs.inline == InlineAttr::Always { + if codegen_fn_attrs.target_features.len() > 0 { + if codegen_fn_attrs.inline == InlineAttr::Always { if let Some(span) = inline_span { tcx.sess.span_err(span, "cannot use #[inline(always)] with \ #[target_feature]"); @@ -1896,5 +1896,5 @@ fn trans_fn_attrs<'a, 'tcx>(tcx: TyCtxt<'a, 'tcx, 'tcx>, id: DefId) -> TransFnAt } } - trans_fn_attrs + codegen_fn_attrs } diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e3f7ff5cb3f..99744d5b4b6 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -1179,7 +1179,7 @@ extern "rust-intrinsic" { ``` Please check you didn't make a mistake in the function's name. All intrinsic -functions are defined in librustc_trans/trans/intrinsic.rs and in +functions are defined in librustc_codegen_llvm/intrinsic.rs and in libcore/intrinsics.rs in the Rust source code. Example: ``` @@ -1209,7 +1209,7 @@ fn main() { ``` Please check you didn't make a mistake in the function's name. All intrinsic -functions are defined in librustc_trans/trans/intrinsic.rs and in +functions are defined in librustc_codegen_llvm/intrinsic.rs and in libcore/intrinsics.rs in the Rust source code. Example: ``` diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index c9a80d47791..90889890e0b 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -219,19 +219,19 @@ pub fn run_core(search_paths: SearchPaths, let mut sess = session::build_session_( sessopts, cpath, diagnostic_handler, codemap, ); - let trans = rustc_driver::get_trans(&sess); - let cstore = Rc::new(CStore::new(trans.metadata_loader())); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs)); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let control = &driver::CompileController::basic(); let krate = panictry!(driver::phase_1_parse_input(control, &sess, &input)); - let name = ::rustc_trans_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input); + let name = ::rustc_codegen_utils::link::find_crate_name(Some(&sess), &krate.attrs, &input); let mut crate_loader = CrateLoader::new(&sess, &cstore, &name); @@ -279,7 +279,7 @@ pub fn run_core(search_paths: SearchPaths, let resolver = RefCell::new(resolver); - abort_on_err(driver::phase_3_run_analysis_passes(&*trans, + abort_on_err(driver::phase_3_run_analysis_passes(&*codegen_backend, control, &sess, &*cstore, diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a14a27d5d61..3ce95c78a90 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -33,7 +33,7 @@ extern crate getopts; extern crate env_logger; extern crate rustc; extern crate rustc_data_structures; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate rustc_driver; extern crate rustc_resolve; extern crate rustc_lint; diff --git a/src/librustdoc/test.rs b/src/librustdoc/test.rs index 7be7ce313fc..f507d0dc09d 100644 --- a/src/librustdoc/test.rs +++ b/src/librustdoc/test.rs @@ -95,12 +95,12 @@ pub fn run(input_path: &Path, let mut sess = session::build_session_( sessopts, Some(input_path.to_owned()), handler, codemap.clone(), ); - let trans = rustc_driver::get_trans(&sess); - let cstore = CStore::new(trans.metadata_loader()); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let krate = panictry!(driver::phase_1_parse_input(&driver::CompileController::basic(), @@ -120,7 +120,7 @@ pub fn run(input_path: &Path, }; let crate_name = crate_name.unwrap_or_else(|| { - ::rustc_trans_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input) + ::rustc_codegen_utils::link::find_crate_name(None, &hir_forest.krate().attrs, &input) }); let mut opts = scrape_test_config(hir_forest.krate()); opts.display_warnings |= display_warnings; @@ -273,8 +273,8 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut sess = session::build_session_( sessopts, None, diagnostic_handler, codemap, ); - let trans = rustc_driver::get_trans(&sess); - let cstore = CStore::new(trans.metadata_loader()); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = CStore::new(codegen_backend.metadata_loader()); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); let outdir = Mutex::new(TempDir::new("rustdoctest").ok().expect("rustdoc needs a tempdir")); @@ -282,7 +282,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let mut control = driver::CompileController::basic(); let mut cfg = config::build_configuration(&sess, config::parse_cfgspecs(cfgs.clone())); - target_features::add_configuration(&mut cfg, &sess, &*trans); + target_features::add_configuration(&mut cfg, &sess, &*codegen_backend); sess.parse_sess.config = cfg; let out = Some(outdir.lock().unwrap().path().to_path_buf()); @@ -293,7 +293,7 @@ fn run_test(test: &str, cratename: &str, filename: &FileName, line: usize, let res = panic::catch_unwind(AssertUnwindSafe(|| { driver::compile_input( - trans, + codegen_backend, &sess, &cstore, &None, diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index f1229520c77..d247b5402ef 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -839,7 +839,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG is just used for rustc unit tests \ and will never be stable", cfg_fn!(rustc_attrs))), - ("rustc_partition_translated", Whitelisted, Gated(Stability::Unstable, + ("rustc_partition_codegened", Whitelisted, Gated(Stability::Unstable, "rustc_attrs", "this attribute \ is just used for rustc unit tests \ @@ -938,7 +938,7 @@ pub const BUILTIN_ATTRIBUTES: &'static [(&'static str, AttributeType, AttributeG // FIXME: #14408 whitelist docs since rustdoc looks at them ("doc", Whitelisted, Ungated), - // FIXME: #14406 these are processed in trans, which happens after the + // FIXME: #14406 these are processed in codegen, which happens after the // lint pass ("cold", Whitelisted, Ungated), ("naked", Whitelisted, Gated(Stability::Unstable, diff --git a/src/libsyntax_ext/format.rs b/src/libsyntax_ext/format.rs index 6b155b6596d..f29cc75664d 100644 --- a/src/libsyntax_ext/format.rs +++ b/src/libsyntax_ext/format.rs @@ -79,7 +79,7 @@ struct Context<'a, 'b: 'a> { /// final generated static argument array. We record the starting indices /// corresponding to each positional argument, and number of references /// consumed so far for each argument, to facilitate correct `Position` - /// mapping in `trans_piece`. In effect this can be seen as a "flattened" + /// mapping in `build_piece`. In effect this can be seen as a "flattened" /// version of `arg_unique_types`. /// /// Again with the example described above in docstring for `args`: @@ -108,7 +108,7 @@ struct Context<'a, 'b: 'a> { /// Current position of the implicit positional arg pointer, as if it /// still existed in this phase of processing. - /// Used only for `all_pieces_simple` tracking in `trans_piece`. + /// Used only for `all_pieces_simple` tracking in `build_piece`. curarg: usize, /// Keep track of invalid references to positional arguments invalid_refs: Vec, @@ -373,7 +373,7 @@ impl<'a, 'b> Context<'a, 'b> { ecx.std_path(&["fmt", "rt", "v1", s]) } - fn trans_count(&self, c: parse::Count) -> P { + fn build_count(&self, c: parse::Count) -> P { let sp = self.macsp; let count = |c, arg| { let mut path = Context::rtpath(self.ecx, "Count"); @@ -401,17 +401,17 @@ impl<'a, 'b> Context<'a, 'b> { } } - /// Translate the accumulated string literals to a literal expression - fn trans_literal_string(&mut self) -> P { + /// Build a literal expression from the accumulated string literals + fn build_literal_string(&mut self) -> P { let sp = self.fmtsp; let s = Symbol::intern(&self.literal); self.literal.clear(); self.ecx.expr_str(sp, s) } - /// Translate a `parse::Piece` to a static `rt::Argument` or append + /// Build a static `rt::Argument` from a `parse::Piece` or append /// to the `literal` string. - fn trans_piece(&mut self, + fn build_piece(&mut self, piece: &parse::Piece, arg_index_consumed: &mut Vec) -> Option> { @@ -422,7 +422,7 @@ impl<'a, 'b> Context<'a, 'b> { None } parse::NextArgument(ref arg) => { - // Translate the position + // Build the position let pos = { let pos = |c, arg| { let mut path = Context::rtpath(self.ecx, "Position"); @@ -486,7 +486,7 @@ impl<'a, 'b> Context<'a, 'b> { self.all_pieces_simple = false; } - // Translate the format + // Build the format let fill = self.ecx.expr_lit(sp, ast::LitKind::Char(fill)); let align = |name| { let mut p = Context::rtpath(self.ecx, "Alignment"); @@ -501,8 +501,8 @@ impl<'a, 'b> Context<'a, 'b> { }; let align = self.ecx.expr_path(align); let flags = self.ecx.expr_u32(sp, arg.format.flags); - let prec = self.trans_count(arg.format.precision); - let width = self.trans_count(arg.format.width); + let prec = self.build_count(arg.format.precision); + let width = self.build_count(arg.format.width); let path = self.ecx.path_global(sp, Context::rtpath(self.ecx, "FormatSpec")); let fmt = self.ecx.expr_struct(sp, @@ -759,8 +759,8 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, let mut arg_index_consumed = vec![0usize; cx.arg_index_map.len()]; for piece in pieces { - if let Some(piece) = cx.trans_piece(&piece, &mut arg_index_consumed) { - let s = cx.trans_literal_string(); + if let Some(piece) = cx.build_piece(&piece, &mut arg_index_consumed) { + let s = cx.build_literal_string(); cx.str_pieces.push(s); cx.pieces.push(piece); } @@ -776,7 +776,7 @@ pub fn expand_preparsed_format_args(ecx: &mut ExtCtxt, return DummyResult::raw_expr(sp); } if !cx.literal.is_empty() { - let s = cx.trans_literal_string(); + let s = cx.build_literal_string(); cx.str_pieces.push(s); } diff --git a/src/libsyntax_ext/format_foreign.rs b/src/libsyntax_ext/format_foreign.rs index 2b8603c75a5..0dd8f0c55d8 100644 --- a/src/libsyntax_ext/format_foreign.rs +++ b/src/libsyntax_ext/format_foreign.rs @@ -697,7 +697,7 @@ pub mod printf { /// Check that the translations are what we expect. #[test] - fn test_trans() { + fn test_translation() { assert_eq_pnsat!("%c", Some("{}")); assert_eq_pnsat!("%d", Some("{}")); assert_eq_pnsat!("%u", Some("{}")); @@ -900,7 +900,7 @@ pub mod shell { } #[test] - fn test_trans() { + fn test_translation() { assert_eq_pnsat!("$0", Some("{0}")); assert_eq_pnsat!("$9", Some("{9}")); assert_eq_pnsat!("$1", Some("{1}")); diff --git a/src/libsyntax_ext/global_asm.rs b/src/libsyntax_ext/global_asm.rs index f01a0aacb0a..642aa0e5b12 100644 --- a/src/libsyntax_ext/global_asm.rs +++ b/src/libsyntax_ext/global_asm.rs @@ -14,7 +14,7 @@ /// "file-scoped", or "module-level" assembly. These synonyms /// all correspond to LLVM's module-level inline assembly instruction. /// -/// For example, `global_asm!("some assembly here")` translates to +/// For example, `global_asm!("some assembly here")` codegens to /// LLVM's `module asm "some assembly here"`. All of LLVM's caveats /// therefore apply. diff --git a/src/rustllvm/PassWrapper.cpp b/src/rustllvm/PassWrapper.cpp index 3b400b879eb..d5410feb254 100644 --- a/src/rustllvm/PassWrapper.cpp +++ b/src/rustllvm/PassWrapper.cpp @@ -388,7 +388,7 @@ extern "C" LLVMTargetMachineRef LLVMRustCreateTargetMachine( Options.FunctionSections = FunctionSections; if (TrapUnreachable) { - // Tell LLVM to translate `unreachable` into an explicit trap instruction. + // Tell LLVM to codegen `unreachable` into an explicit trap instruction. // This limits the extent of possible undefined behavior in some cases, as // it prevents control flow from "falling through" into whatever code // happens to be laid out next in memory. diff --git a/src/test/codegen-units/item-collection/cross-crate-closures.rs b/src/test/codegen-units/item-collection/cross-crate-closures.rs index 320be278198..a26604d3ce9 100644 --- a/src/test/codegen-units/item-collection/cross-crate-closures.rs +++ b/src/test/codegen-units/item-collection/cross-crate-closures.rs @@ -9,12 +9,12 @@ // except according to those terms. // In the current version of the collector that still has to support -// legacy-trans, closures do not generate their own TransItems, so we are -// ignoring this test until MIR trans has taken over completely +// legacy-codegen, closures do not generate their own MonoItems, so we are +// ignoring this test until MIR codegen has taken over completely // ignore-test // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -22,16 +22,16 @@ // aux-build:cgu_extern_closures.rs extern crate cgu_extern_closures; -//~ TRANS_ITEM fn cross_crate_closures::start[0] +//~ MONO_ITEM fn cross_crate_closures::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0] - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn[0]::{{closure}}[0] let _ = cgu_extern_closures::inlined_fn(1, 2); - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0] - //~ TRANS_ITEM fn cgu_extern_closures::inlined_fn_generic[0]::{{closure}}[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn_generic[0] + //~ MONO_ITEM fn cgu_extern_closures::inlined_fn_generic[0]::{{closure}}[0] let _ = cgu_extern_closures::inlined_fn_generic(3, 4, 5i32); // Nothing should be generated for this call, we just link to the instance @@ -41,4 +41,4 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM drop-glue i8 +//~ MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs index bcb3b7b1dad..aa33c25da9a 100644 --- a/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs +++ b/src/test/codegen-units/item-collection/cross-crate-generic-functions.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -17,15 +17,15 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ TRANS_ITEM fn cross_crate_generic_functions::start[0] +//~ MONO_ITEM fn cross_crate_generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn cgu_generic_function::bar[0] - //~ TRANS_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar[0] + //~ MONO_ITEM fn cgu_generic_function::foo[0] let _ = cgu_generic_function::foo(1u32); - //~ TRANS_ITEM fn cgu_generic_function::bar[0] - //~ TRANS_ITEM fn cgu_generic_function::foo[0] + //~ MONO_ITEM fn cgu_generic_function::bar[0] + //~ MONO_ITEM fn cgu_generic_function::foo[0] let _ = cgu_generic_function::foo(2u64); // This should not introduce a codegen item diff --git a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs index 910ae000848..7a16b22a023 100644 --- a/src/test/codegen-units/item-collection/cross-crate-trait-method.rs +++ b/src/test/codegen-units/item-collection/cross-crate-trait-method.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -19,7 +19,7 @@ extern crate cgu_export_trait_method; use cgu_export_trait_method::Trait; -//~ TRANS_ITEM fn cross_crate_trait_method::start[0] +//~ MONO_ITEM fn cross_crate_trait_method::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { // The object code of these methods is contained in the external crate, so @@ -30,31 +30,31 @@ fn start(_: isize, _: *const *const u8) -> isize { // Currently, no object code is generated for trait methods with default // implemenations, unless they are actually called from somewhere. Therefore // we cannot import the implementations and have to create our own inline. - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] let _ = Trait::with_default_impl(0u32); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl[0] let _ = Trait::with_default_impl('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic(0u32, "abc"); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic(0u32, false); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic('x', 1i16); - //~ TRANS_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::Trait[0]::with_default_impl_generic[0] let _ = Trait::with_default_impl_generic('y', 0i32); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] let _: (u32, char) = Trait::without_default_impl_generic('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[1]::without_default_impl_generic[0] let _: (u32, bool) = Trait::without_default_impl_generic(false); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] let _: (char, char) = Trait::without_default_impl_generic('c'); - //~ TRANS_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] + //~ MONO_ITEM fn cgu_export_trait_method::{{impl}}[0]::without_default_impl_generic[0] let _: (char, bool) = Trait::without_default_impl_generic(false); 0 diff --git a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs index 52af8165032..49e4b8d43c1 100644 --- a/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs +++ b/src/test/codegen-units/item-collection/drop_in_place_intrinsic.rs @@ -9,24 +9,24 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ drop_in_place_intrinsic0[Internal] struct StructWithDtor(u32); impl Drop for StructWithDtor { - //~ TRANS_ITEM fn drop_in_place_intrinsic::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn drop_in_place_intrinsic::{{impl}}[0]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn drop_in_place_intrinsic::start[0] +//~ MONO_ITEM fn drop_in_place_intrinsic::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]; 2]> @@ drop_in_place_intrinsic0[Internal] let x = [StructWithDtor(0), StructWithDtor(1)]; drop_slice_in_place(&x); @@ -34,13 +34,13 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM fn drop_in_place_intrinsic::drop_slice_in_place[0] +//~ MONO_ITEM fn drop_in_place_intrinsic::drop_slice_in_place[0] fn drop_slice_in_place(x: &[StructWithDtor]) { unsafe { // This is the interesting thing in this test case: Normally we would // not have drop-glue for the unsized [StructWithDtor]. This has to be // generated though when the drop_in_place() intrinsic is used. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<[drop_in_place_intrinsic::StructWithDtor[0]]> @@ drop_in_place_intrinsic0[Internal] ::std::ptr::drop_in_place(x as *const _ as *mut [StructWithDtor]); } } diff --git a/src/test/codegen-units/item-collection/function-as-argument.rs b/src/test/codegen-units/item-collection/function-as-argument.rs index 65707c1aa4d..9a88336d1e5 100644 --- a/src/test/codegen-units/item-collection/function-as-argument.rs +++ b/src/test/codegen-units/item-collection/function-as-argument.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -24,26 +24,26 @@ fn take_fn_pointer(f: fn(T1, T2), x: T1, y: T2) { (f)(x, y) } -//~ TRANS_ITEM fn function_as_argument::start[0] +//~ MONO_ITEM fn function_as_argument::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] - //~ TRANS_ITEM fn function_as_argument::function[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn function_as_argument::take_fn_once[0] + //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] take_fn_once(function, 0u32, "abc"); - //~ TRANS_ITEM fn function_as_argument::take_fn_once[0] - //~ TRANS_ITEM fn function_as_argument::function[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] + //~ MONO_ITEM fn function_as_argument::take_fn_once[0] + //~ MONO_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] take_fn_once(function, 'c', 0f64); - //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] - //~ TRANS_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] + //~ MONO_ITEM fn function_as_argument::function[0] take_fn_pointer(function, 0i32, ()); - //~ TRANS_ITEM fn function_as_argument::take_fn_pointer[0] - //~ TRANS_ITEM fn function_as_argument::function[0] + //~ MONO_ITEM fn function_as_argument::take_fn_pointer[0] + //~ MONO_ITEM fn function_as_argument::function[0] take_fn_pointer(function, 0f32, 0i64); 0 diff --git a/src/test/codegen-units/item-collection/generic-drop-glue.rs b/src/test/codegen-units/item-collection/generic-drop-glue.rs index d3d9aa3aefc..aad32d1eb7c 100644 --- a/src/test/codegen-units/item-collection/generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/generic-drop-glue.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -47,22 +47,22 @@ enum EnumNoDrop { struct NonGenericNoDrop(i32); struct NonGenericWithDrop(i32); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ generic_drop_glue0[Internal] impl Drop for NonGenericWithDrop { - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[2]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn generic_drop_glue::start[0] +//~ MONO_ITEM fn generic_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0] let _ = StructWithDrop { x: 0i8, y: 'a' }.x; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[0]::drop[0]<&str, generic_drop_glue::NonGenericNoDrop[0]> let _ = StructWithDrop { x: "&str", y: NonGenericNoDrop(0) }.y; // Should produce no drop glue @@ -70,18 +70,18 @@ fn start(_: isize, _: *const *const u8) -> isize { // This is supposed to generate drop-glue because it contains a field that // needs to be dropped. - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] let _ = StructNoDrop { x: NonGenericWithDrop(0), y: 0f64 }.y; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::A::(0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as i32 }; - //~TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] - //~ TRANS_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] + //~MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ generic_drop_glue0[Internal] + //~ MONO_ITEM fn generic_drop_glue::{{impl}}[1]::drop[0] let _ = match EnumWithDrop::B::(1.0) { EnumWithDrop::A(x) => x, EnumWithDrop::B(x) => x as f64 diff --git a/src/test/codegen-units/item-collection/generic-functions.rs b/src/test/codegen-units/item-collection/generic-functions.rs index 8efe4b2762a..402d19f9996 100644 --- a/src/test/codegen-units/item-collection/generic-functions.rs +++ b/src/test/codegen-units/item-collection/generic-functions.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -27,39 +27,39 @@ fn foo3(a: T1, b: T2, c: T3) -> (T1, T2, T3) { } // This function should be instantiated even if no used -//~ TRANS_ITEM fn generic_functions::lifetime_only[0] +//~ MONO_ITEM fn generic_functions::lifetime_only[0] pub fn lifetime_only<'a>(a: &'a u32) -> &'a u32 { a } -//~ TRANS_ITEM fn generic_functions::start[0] +//~ MONO_ITEM fn generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1(2i32); - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1(2i64); - //~ TRANS_ITEM fn generic_functions::foo1[0]<&str> + //~ MONO_ITEM fn generic_functions::foo1[0]<&str> let _ = foo1("abc"); - //~ TRANS_ITEM fn generic_functions::foo1[0] + //~ MONO_ITEM fn generic_functions::foo1[0] let _ = foo1('v'); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2(2i32, 2i32); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2(2i64, "abc"); - //~ TRANS_ITEM fn generic_functions::foo2[0]<&str, usize> + //~ MONO_ITEM fn generic_functions::foo2[0]<&str, usize> let _ = foo2("a", 2usize); - //~ TRANS_ITEM fn generic_functions::foo2[0] + //~ MONO_ITEM fn generic_functions::foo2[0] let _ = foo2('v', ()); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(2i32, 2i32, 2i32); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(2i64, "abc", 'c'); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3(0i16, "a", 2usize); - //~ TRANS_ITEM fn generic_functions::foo3[0] + //~ MONO_ITEM fn generic_functions::foo3[0] let _ = foo3('v', (), ()); 0 diff --git a/src/test/codegen-units/item-collection/generic-impl.rs b/src/test/codegen-units/item-collection/generic-impl.rs index d1ee8ee624c..07e51a1e947 100644 --- a/src/test/codegen-units/item-collection/generic-impl.rs +++ b/src/test/codegen-units/item-collection/generic-impl.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -41,41 +41,41 @@ pub struct LifeTimeOnly<'a> { impl<'a> LifeTimeOnly<'a> { - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::foo[0] pub fn foo(&self) {} - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::bar[0] pub fn bar(&'a self) {} - //~ TRANS_ITEM fn generic_impl::{{impl}}[1]::baz[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[1]::baz[0] pub fn baz<'b>(&'b self) {} pub fn non_instantiated(&self) {} } -//~ TRANS_ITEM fn generic_impl::start[0] +//~ MONO_ITEM fn generic_impl::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new(0i32).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new(0i64).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0] - //~ TRANS_ITEM fn generic_impl::id[0] - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0] + //~ MONO_ITEM fn generic_impl::id[0] + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0] let _ = Struct::new('c').get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str> - //~ TRANS_ITEM fn generic_impl::id[0]<&str> - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::get[0], i16> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]<&str> + //~ MONO_ITEM fn generic_impl::id[0]<&str> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::get[0], i16> let _ = Struct::new(Struct::new("str")).get(0i16); - //~ TRANS_ITEM fn generic_impl::{{impl}}[0]::new[0]> - //~ TRANS_ITEM fn generic_impl::id[0]> + //~ MONO_ITEM fn generic_impl::{{impl}}[0]::new[0]> + //~ MONO_ITEM fn generic_impl::id[0]> let _ = (Struct::new(Struct::new("str")).f)(Struct::new("str")); 0 diff --git a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs index c07d26c3f8d..6d65c0d3aa8 100644 --- a/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs +++ b/src/test/codegen-units/item-collection/impl-in-non-instantiated-generic.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -22,14 +22,14 @@ trait SomeTrait { // discovered. pub fn generic_function(x: T) -> (T, i32) { impl SomeTrait for i64 { - //~ TRANS_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn impl_in_non_instantiated_generic::generic_function[0]::{{impl}}[0]::foo[0] fn foo(&self) {} } (x, 0) } -//~ TRANS_ITEM fn impl_in_non_instantiated_generic::start[0] +//~ MONO_ITEM fn impl_in_non_instantiated_generic::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { 0i64.foo(); diff --git a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs index 2e1138ef128..5c6201da252 100644 --- a/src/test/codegen-units/item-collection/instantiation-through-vtable.rs +++ b/src/test/codegen-units/item-collection/instantiation-through-vtable.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -29,20 +29,20 @@ impl Trait for Struct { fn bar(&self) {} } -//~ TRANS_ITEM fn instantiation_through_vtable::start[0] +//~ MONO_ITEM fn instantiation_through_vtable::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let s1 = Struct { _a: 0u32 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; let s1 = Struct { _a: 0u64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ instantiation_through_vtable0[Internal] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn instantiation_through_vtable::{{impl}}[0]::bar[0] let _ = &s1 as &Trait; 0 diff --git a/src/test/codegen-units/item-collection/items-within-generic-items.rs b/src/test/codegen-units/item-collection/items-within-generic-items.rs index 04b54de3ce2..f9813063831 100644 --- a/src/test/codegen-units/item-collection/items-within-generic-items.rs +++ b/src/test/codegen-units/item-collection/items-within-generic-items.rs @@ -9,19 +9,19 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] fn generic_fn(a: T) -> (T, i32) { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[0] fn nested_fn(a: i32) -> i32 { a + 1 } let x = { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0]::nested_fn[1] fn nested_fn(a: i32) -> i32 { a + 2 } @@ -32,14 +32,14 @@ fn generic_fn(a: T) -> (T, i32) { return (a, x + nested_fn(0)); } -//~ TRANS_ITEM fn items_within_generic_items::start[0] +//~ MONO_ITEM fn items_within_generic_items::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0i64); - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0u16); - //~ TRANS_ITEM fn items_within_generic_items::generic_fn[0] + //~ MONO_ITEM fn items_within_generic_items::generic_fn[0] let _ = generic_fn(0i8); 0 diff --git a/src/test/codegen-units/item-collection/non-generic-closures.rs b/src/test/codegen-units/item-collection/non-generic-closures.rs index f0121d56cec..77ada23de71 100644 --- a/src/test/codegen-units/item-collection/non-generic-closures.rs +++ b/src/test/codegen-units/item-collection/non-generic-closures.rs @@ -9,51 +9,51 @@ // except according to those terms. // In the current version of the collector that still has to support -// legacy-trans, closures do not generate their own TransItems, so we are -// ignoring this test until MIR trans has taken over completely +// legacy-codegen, closures do not generate their own MonoItems, so we are +// ignoring this test until MIR codegen has taken over completely // ignore-test // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn non_generic_closures::temporary[0] +//~ MONO_ITEM fn non_generic_closures::temporary[0] fn temporary() { - //~ TRANS_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::temporary[0]::{{closure}}[0] (|a: u32| { let _ = a; })(4); } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0] fn assigned_to_variable_but_not_executed() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_but_not_executed[0]::{{closure}}[0] let _x = |a: i16| { let _ = a + 1; }; } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0] fn assigned_to_variable_executed_indirectly() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_directly[0]::{{closure}}[0] let f = |a: i32| { let _ = a + 2; }; run_closure(&f); } -//~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] +//~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0] fn assigned_to_variable_executed_directly() { - //~ TRANS_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] + //~ MONO_ITEM fn non_generic_closures::assigned_to_variable_executed_indirectly[0]::{{closure}}[0] let f = |a: i64| { let _ = a + 3; }; f(4); } -//~ TRANS_ITEM fn non_generic_closures::start[0] +//~ MONO_ITEM fn non_generic_closures::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { temporary(); @@ -64,7 +64,7 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM fn non_generic_closures::run_closure[0] +//~ MONO_ITEM fn non_generic_closures::run_closure[0] fn run_closure(f: &Fn(i32)) { f(3); } diff --git a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs index bf084aa96ea..6ca24aa5b4b 100644 --- a/src/test/codegen-units/item-collection/non-generic-drop-glue.rs +++ b/src/test/codegen-units/item-collection/non-generic-drop-glue.rs @@ -9,19 +9,19 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] struct StructWithDrop { x: i32 } impl Drop for StructWithDrop { - //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } @@ -29,13 +29,13 @@ struct StructNoDrop { x: i32 } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ non_generic_drop_glue0[Internal] enum EnumWithDrop { A(i32) } impl Drop for EnumWithDrop { - //~ TRANS_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn non_generic_drop_glue::{{impl}}[1]::drop[0] fn drop(&mut self) {} } @@ -43,7 +43,7 @@ enum EnumNoDrop { A(i32) } -//~ TRANS_ITEM fn non_generic_drop_glue::start[0] +//~ MONO_ITEM fn non_generic_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = StructWithDrop { x: 0 }.x; diff --git a/src/test/codegen-units/item-collection/non-generic-functions.rs b/src/test/codegen-units/item-collection/non-generic-functions.rs index 8c487db5c96..38d08c8a6ed 100644 --- a/src/test/codegen-units/item-collection/non-generic-functions.rs +++ b/src/test/codegen-units/item-collection/non-generic-functions.rs @@ -9,29 +9,29 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn non_generic_functions::foo[0] +//~ MONO_ITEM fn non_generic_functions::foo[0] fn foo() { { - //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::foo[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::foo[0]::foo[1] fn foo() {} foo(); } } -//~ TRANS_ITEM fn non_generic_functions::bar[0] +//~ MONO_ITEM fn non_generic_functions::bar[0] fn bar() { - //~ TRANS_ITEM fn non_generic_functions::bar[0]::baz[0] + //~ MONO_ITEM fn non_generic_functions::bar[0]::baz[0] fn baz() {} baz(); } @@ -39,38 +39,38 @@ fn bar() { struct Struct { _x: i32 } impl Struct { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0] fn foo() { { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::foo[0]::foo[1] fn foo() {} foo(); } } - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0] fn bar(&self) { { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[0] fn foo() {} foo(); } { - //~ TRANS_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1] + //~ MONO_ITEM fn non_generic_functions::{{impl}}[0]::bar[0]::foo[1] fn foo() {} foo(); } } } -//~ TRANS_ITEM fn non_generic_functions::start[0] +//~ MONO_ITEM fn non_generic_functions::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); diff --git a/src/test/codegen-units/item-collection/overloaded-operators.rs b/src/test/codegen-units/item-collection/overloaded-operators.rs index 05848a727e9..dae6ef83551 100644 --- a/src/test/codegen-units/item-collection/overloaded-operators.rs +++ b/src/test/codegen-units/item-collection/overloaded-operators.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![crate_type="lib"] @@ -23,7 +23,7 @@ pub struct Indexable { impl Index for Indexable { type Output = u8; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[0]::index[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[0]::index[0] fn index(&self, index: usize) -> &Self::Output { if index >= 3 { &self.data[0] @@ -34,7 +34,7 @@ impl Index for Indexable { } impl IndexMut for Indexable { - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[1]::index_mut[0] fn index_mut(&mut self, index: usize) -> &mut Self::Output { if index >= 3 { &mut self.data[0] @@ -45,8 +45,8 @@ impl IndexMut for Indexable { } -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::eq[0] -//~ TRANS_ITEM fn overloaded_operators::{{impl}}[4]::ne[0] +//~ MONO_ITEM fn overloaded_operators::{{impl}}[4]::eq[0] +//~ MONO_ITEM fn overloaded_operators::{{impl}}[4]::ne[0] #[derive(PartialEq)] pub struct Equatable(u32); @@ -54,7 +54,7 @@ pub struct Equatable(u32); impl Add for Equatable { type Output = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[2]::add[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[2]::add[0] fn add(self, rhs: u32) -> u32 { self.0 + rhs } @@ -63,7 +63,7 @@ impl Add for Equatable { impl Deref for Equatable { type Target = u32; - //~ TRANS_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] + //~ MONO_ITEM fn overloaded_operators::{{impl}}[3]::deref[0] fn deref(&self) -> &Self::Target { &self.0 } diff --git a/src/test/codegen-units/item-collection/static-init.rs b/src/test/codegen-units/item-collection/static-init.rs index 5ff7c3480b1..f36c4903458 100644 --- a/src/test/codegen-units/item-collection/static-init.rs +++ b/src/test/codegen-units/item-collection/static-init.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // ignore-tidy-linelength #![feature(start)] @@ -17,12 +17,11 @@ pub static FN : fn() = foo::; pub fn foo() { } -//~ TRANS_ITEM fn static_init::foo[0] -//~ TRANS_ITEM static static_init::FN[0] +//~ MONO_ITEM fn static_init::foo[0] +//~ MONO_ITEM static static_init::FN[0] -//~ TRANS_ITEM fn static_init::start[0] +//~ MONO_ITEM fn static_init::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { 0 } - diff --git a/src/test/codegen-units/item-collection/statics-and-consts.rs b/src/test/codegen-units/item-collection/statics-and-consts.rs index 11df1da3a78..883809ff059 100644 --- a/src/test/codegen-units/item-collection/statics-and-consts.rs +++ b/src/test/codegen-units/item-collection/statics-and-consts.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -48,7 +48,7 @@ fn foo() { }; } -//~ TRANS_ITEM fn statics_and_consts::start[0] +//~ MONO_ITEM fn statics_and_consts::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { foo(); @@ -57,9 +57,9 @@ fn start(_: isize, _: *const *const u8) -> isize { 0 } -//~ TRANS_ITEM static statics_and_consts::STATIC1[0] +//~ MONO_ITEM static statics_and_consts::STATIC1[0] -//~ TRANS_ITEM fn statics_and_consts::foo[0] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[0] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[1] -//~ TRANS_ITEM static statics_and_consts::foo[0]::STATIC2[2] +//~ MONO_ITEM fn statics_and_consts::foo[0] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[0] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[1] +//~ MONO_ITEM static statics_and_consts::foo[0]::STATIC2[2] diff --git a/src/test/codegen-units/item-collection/trait-implementations.rs b/src/test/codegen-units/item-collection/trait-implementations.rs index 8eb33dd647f..f85486b5d34 100644 --- a/src/test/codegen-units/item-collection/trait-implementations.rs +++ b/src/test/codegen-units/item-collection/trait-implementations.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -21,7 +21,7 @@ pub trait SomeTrait { impl SomeTrait for i64 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[0]::foo[0] fn foo(&self) {} fn bar(&self, _: T) {} @@ -29,7 +29,7 @@ impl SomeTrait for i64 { impl SomeTrait for i32 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::foo[0] fn foo(&self) {} fn bar(&self, _: T) {} @@ -43,7 +43,7 @@ pub trait SomeGenericTrait { // Concrete impl of generic trait impl SomeGenericTrait for f64 { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::foo[0] fn foo(&self, _: u32) {} fn bar(&self, _: u32, _: T2) {} @@ -56,28 +56,28 @@ impl SomeGenericTrait for f32 { fn bar(&self, _: T, _: T2) {} } -//~ TRANS_ITEM fn trait_implementations::start[0] +//~ MONO_ITEM fn trait_implementations::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_implementations::{{impl}}[1]::bar[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[1]::bar[0] 0i32.bar('x'); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str> + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<&str> 0f64.bar(0u32, "&str"); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()> + //~ MONO_ITEM fn trait_implementations::{{impl}}[2]::bar[0]<()> 0f64.bar(0u32, ()); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] 0f32.foo('x'); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::foo[0] 0f32.foo(-1i64); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0] + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0] 0f32.bar(0u32, ()); - //~ TRANS_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> + //~ MONO_ITEM fn trait_implementations::{{impl}}[3]::bar[0]<&str, &str> 0f32.bar("&str", "&str"); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-as-argument.rs b/src/test/codegen-units/item-collection/trait-method-as-argument.rs index 10b21630308..1f08f0f8060 100644 --- a/src/test/codegen-units/item-collection/trait-method-as-argument.rs +++ b/src/test/codegen-units/item-collection/trait-method-as-argument.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -37,33 +37,33 @@ fn take_foo_mut T>(mut f: F, arg: T) -> T { (f)(arg) } -//~ TRANS_ITEM fn trait_method_as_argument::start[0] +//~ MONO_ITEM fn trait_method_as_argument::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] u32> - //~ TRANS_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] u32> + //~ MONO_ITEM fn trait_method_as_argument::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] u32, (u32)> take_foo_once(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_once[0] char> - //~ TRANS_ITEM fn trait_method_as_argument::Trait[0]::foo[0] - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_once[0] char> + //~ MONO_ITEM fn trait_method_as_argument::Trait[0]::foo[0] + //~ MONO_ITEM fn core::ops[0]::function[0]::FnOnce[0]::call_once[0] char, (char)> take_foo_once(Trait::foo, 'c'); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] u32> - //~ TRANS_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] u32> + //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] u32, (u32)> take_foo(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo[0] char> - //~ TRANS_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo[0] char> + //~ MONO_ITEM fn core::ops[0]::function[0]::Fn[0]::call[0] char, (char)> take_foo(Trait::foo, 'c'); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] char, (char)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] u32> + //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] char, (char)> take_foo_mut(Trait::foo, 0u32); - //~ TRANS_ITEM fn trait_method_as_argument::take_foo_mut[0] char> - //~ TRANS_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] u32, (u32)> + //~ MONO_ITEM fn trait_method_as_argument::take_foo_mut[0] char> + //~ MONO_ITEM fn core::ops[0]::function[0]::FnMut[0]::call_mut[0] u32, (u32)> take_foo_mut(Trait::foo, 'c'); 0 diff --git a/src/test/codegen-units/item-collection/trait-method-default-impl.rs b/src/test/codegen-units/item-collection/trait-method-default-impl.rs index a6ae3765b2e..b130747972e 100644 --- a/src/test/codegen-units/item-collection/trait-method-default-impl.rs +++ b/src/test/codegen-units/item-collection/trait-method-default-impl.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![deny(dead_code)] #![feature(start)] @@ -24,7 +24,7 @@ impl SomeTrait for i8 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::foo[0] } trait SomeGenericTrait { @@ -38,7 +38,7 @@ impl SomeGenericTrait for i32 { // For the non-generic foo(), we should generate a codegen-item even if it // is not called anywhere - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::foo[0] } // Non-generic impl of generic trait @@ -47,25 +47,25 @@ impl SomeGenericTrait for u32 { // since nothing is monomorphic here, nothing should be generated unless used somewhere. } -//~ TRANS_ITEM fn trait_method_default_impl::start[0] +//~ MONO_ITEM fn trait_method_default_impl::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] let _ = 1i8.bar('c'); - //~ TRANS_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeTrait[0]::bar[0] let _ = 2i8.bar("&str"); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0i32.bar(0u64, 'c'); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0i32.bar(0u64, "&str"); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0u32.bar(0i8, &['c']); - //~ TRANS_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] + //~ MONO_ITEM fn trait_method_default_impl::SomeGenericTrait[0]::bar[0] 0u32.bar(0i16, ()); 0 diff --git a/src/test/codegen-units/item-collection/transitive-drop-glue.rs b/src/test/codegen-units/item-collection/transitive-drop-glue.rs index 57cd10187a2..d20213c109b 100644 --- a/src/test/codegen-units/item-collection/transitive-drop-glue.rs +++ b/src/test/codegen-units/item-collection/transitive-drop-glue.rs @@ -9,21 +9,21 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Root(Intermediate); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Intermediate(Leaf); -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ transitive_drop_glue0[Internal] struct Leaf; impl Drop for Leaf { - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } @@ -35,21 +35,21 @@ impl Drop for LeafGen { fn drop(&mut self) {} } -//~ TRANS_ITEM fn transitive_drop_glue::start[0] +//~ MONO_ITEM fn transitive_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { let _ = Root(Intermediate(Leaf)); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0u32))); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] - //~ TRANS_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]> @@ transitive_drop_glue0[Internal] + //~ MONO_ITEM fn transitive_drop_glue::{{impl}}[1]::drop[0] let _ = RootGen(IntermediateGen(LeafGen(0i16))); 0 diff --git a/src/test/codegen-units/item-collection/tuple-drop-glue.rs b/src/test/codegen-units/item-collection/tuple-drop-glue.rs index a5f2409b8ae..9e4cc6ea6f0 100644 --- a/src/test/codegen-units/item-collection/tuple-drop-glue.rs +++ b/src/test/codegen-units/item-collection/tuple-drop-glue.rs @@ -9,28 +9,28 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] #![feature(start)] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue0[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ tuple_drop_glue0[Internal] struct Dropped; impl Drop for Dropped { - //~ TRANS_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0] + //~ MONO_ITEM fn tuple_drop_glue::{{impl}}[0]::drop[0] fn drop(&mut self) {} } -//~ TRANS_ITEM fn tuple_drop_glue::start[0] +//~ MONO_ITEM fn tuple_drop_glue::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, tuple_drop_glue::Dropped[0])> @@ tuple_drop_glue0[Internal] let x = (0u32, Dropped); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(i16, (tuple_drop_glue::Dropped[0], bool))> @@ tuple_drop_glue0[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(tuple_drop_glue::Dropped[0], bool)> @@ tuple_drop_glue0[Internal] let x = (0i16, (Dropped, true)); 0 diff --git a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs index 59b25d8beca..c2ff846721c 100644 --- a/src/test/codegen-units/item-collection/unreferenced-const-fn.rs +++ b/src/test/codegen-units/item-collection/unreferenced-const-fn.rs @@ -9,9 +9,9 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=lazy +// compile-flags:-Zprint-mono-items=lazy -// NB: We do not expect *any* translation item to be generated here. +// NB: We do not expect *any* monomorphization to be generated here. #![feature(const_fn)] #![deny(dead_code)] diff --git a/src/test/codegen-units/item-collection/unreferenced-inline-function.rs b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs index 75d41a38012..829b4fbf3c9 100644 --- a/src/test/codegen-units/item-collection/unreferenced-inline-function.rs +++ b/src/test/codegen-units/item-collection/unreferenced-inline-function.rs @@ -9,9 +9,9 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=lazy +// compile-flags:-Zprint-mono-items=lazy -// NB: We do not expect *any* translation item to be generated here. +// NB: We do not expect *any* monomorphization to be generated here. #![deny(dead_code)] #![crate_type = "rlib"] @@ -20,4 +20,3 @@ pub fn foo() -> bool { [1, 2] == [3, 4] } - diff --git a/src/test/codegen-units/item-collection/unsizing.rs b/src/test/codegen-units/item-collection/unsizing.rs index 87d2581e1f8..adc0eb6c709 100644 --- a/src/test/codegen-units/item-collection/unsizing.rs +++ b/src/test/codegen-units/item-collection/unsizing.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager // compile-flags:-Zinline-in-all-cgus #![deny(dead_code)] @@ -54,19 +54,19 @@ struct Wrapper(*const T); impl, U: ?Sized> CoerceUnsized> for Wrapper {} -//~ TRANS_ITEM fn unsizing::start[0] +//~ MONO_ITEM fn unsizing::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { // simple case let bool_sized = &true; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[0]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[0]::foo[0] let _bool_unsized = bool_sized as &Trait; let char_sized = &'a'; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[1]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[1]::foo[0] let _char_unsized = char_sized as &Trait; // struct field @@ -75,14 +75,14 @@ fn start(_: isize, _: *const *const u8) -> isize { _b: 2, _c: 3.0f64 }; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[2]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[2]::foo[0] let _struct_unsized = struct_sized as &Struct; // custom coercion let wrapper_sized = Wrapper(&0u32); - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] - //~ TRANS_ITEM fn unsizing::{{impl}}[3]::foo[0] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ unsizing0[Internal] + //~ MONO_ITEM fn unsizing::{{impl}}[3]::foo[0] let _wrapper_sized = wrapper_sized as Wrapper; 0 diff --git a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs index ce85c4fc13c..2edc7b211a1 100644 --- a/src/test/codegen-units/item-collection/unused-traits-and-generics.rs +++ b/src/test/codegen-units/item-collection/unused-traits-and-generics.rs @@ -9,7 +9,7 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager +// compile-flags:-Zprint-mono-items=eager #![crate_type="lib"] #![deny(dead_code)] @@ -85,4 +85,4 @@ impl NonGeneric { } // Only the non-generic methods should be instantiated: -//~ TRANS_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] +//~ MONO_ITEM fn unused_traits_and_generics::{{impl}}[3]::foo[0] diff --git a/src/test/codegen-units/partitioning/extern-drop-glue.rs b/src/test/codegen-units/partitioning/extern-drop-glue.rs index da96c5e183d..cbad3a63884 100644 --- a/src/test/codegen-units/partitioning/extern-drop-glue.rs +++ b/src/test/codegen-units/partitioning/extern-drop-glue.rs @@ -12,7 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/extern-drop-glue // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -21,14 +21,14 @@ // aux-build:cgu_extern_drop_glue.rs extern crate cgu_extern_drop_glue; -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] extern_drop_glue-mod1[Internal] struct LocalStruct(cgu_extern_drop_glue::Struct); -//~ TRANS_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] +//~ MONO_ITEM fn extern_drop_glue::user[0] @@ extern_drop_glue[External] pub fn user() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } @@ -37,10 +37,10 @@ pub mod mod1 { struct LocalStruct(cgu_extern_drop_glue::Struct); - //~ TRANS_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] + //~ MONO_ITEM fn extern_drop_glue::mod1[0]::user[0] @@ extern_drop_glue-mod1[External] pub fn user() { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ extern_drop_glue-mod1[Internal] let _ = LocalStruct(cgu_extern_drop_glue::Struct(0)); } } diff --git a/src/test/codegen-units/partitioning/extern-generic.rs b/src/test/codegen-units/partitioning/extern-generic.rs index 140b43c85d5..a774376690a 100644 --- a/src/test/codegen-units/partitioning/extern-generic.rs +++ b/src/test/codegen-units/partitioning/extern-generic.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/extern-generic -Zshare-generics=y #![allow(dead_code)] #![crate_type="lib"] @@ -19,7 +19,7 @@ // aux-build:cgu_generic_function.rs extern crate cgu_generic_function; -//~ TRANS_ITEM fn extern_generic::user[0] @@ extern_generic[Internal] +//~ MONO_ITEM fn extern_generic::user[0] @@ extern_generic[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -27,7 +27,7 @@ fn user() { mod mod1 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[Internal] + //~ MONO_ITEM fn extern_generic::mod1[0]::user[0] @@ extern_generic-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -35,7 +35,7 @@ mod mod1 { mod mod1 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn extern_generic::mod1[0]::mod1[0]::user[0] @@ extern_generic-mod1-mod1[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } @@ -45,18 +45,18 @@ mod mod1 { mod mod2 { use cgu_generic_function; - //~ TRANS_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[Internal] + //~ MONO_ITEM fn extern_generic::mod2[0]::user[0] @@ extern_generic-mod2[Internal] fn user() { let _ = cgu_generic_function::foo("abc"); } } mod mod3 { - //~ TRANS_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[Internal] + //~ MONO_ITEM fn extern_generic::mod3[0]::non_user[0] @@ extern_generic-mod3[Internal] fn non_user() {} } // Make sure the two generic functions from the extern crate get instantiated // once for the current crate -//~ TRANS_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] -//~ TRANS_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::foo[0]<&str> @@ cgu_generic_function.volatile[External] +//~ MONO_ITEM fn cgu_generic_function::bar[0]<&str> @@ cgu_generic_function.volatile[External] diff --git a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs index 01600c03ba2..4136557d800 100644 --- a/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs +++ b/src/test/codegen-units/partitioning/inlining-from-extern-crate.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/inlining-from-extern-crate // compile-flags:-Zinline-in-all-cgus #![crate_type="lib"] @@ -22,28 +22,28 @@ extern crate cgu_explicit_inlining; // This test makes sure that items inlined from external crates are privately // instantiated in every codegen unit they are used in. -//~ TRANS_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] -//~ TRANS_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod1[Internal] +//~ MONO_ITEM fn cgu_explicit_inlining::always_inlined[0] @@ inlining_from_extern_crate[Internal] inlining_from_extern_crate-mod2[Internal] -//~ TRANS_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[External] +//~ MONO_ITEM fn inlining_from_extern_crate::user[0] @@ inlining_from_extern_crate[External] pub fn user() { cgu_explicit_inlining::inlined(); cgu_explicit_inlining::always_inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } pub mod mod1 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] + //~ MONO_ITEM fn inlining_from_extern_crate::mod1[0]::user[0] @@ inlining_from_extern_crate-mod1[External] pub fn user() { cgu_explicit_inlining::inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } } @@ -51,12 +51,12 @@ pub mod mod1 { pub mod mod2 { use cgu_explicit_inlining; - //~ TRANS_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] + //~ MONO_ITEM fn inlining_from_extern_crate::mod2[0]::user[0] @@ inlining_from_extern_crate-mod2[External] pub fn user() { cgu_explicit_inlining::always_inlined(); - // does not generate a translation item in this crate + // does not generate a monomorphization in this crate cgu_explicit_inlining::never_inlined(); } } diff --git a/src/test/codegen-units/partitioning/local-drop-glue.rs b/src/test/codegen-units/partitioning/local-drop-glue.rs index f7c05285ed6..98729d99ea9 100644 --- a/src/test/codegen-units/partitioning/local-drop-glue.rs +++ b/src/test/codegen-units/partitioning/local-drop-glue.rs @@ -11,28 +11,28 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-drop-glue // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] #![crate_type="rlib"] -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] local_drop_glue-mod1[Internal] struct Struct { _a: u32 } impl Drop for Struct { - //~ TRANS_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue[External] + //~ MONO_ITEM fn local_drop_glue::{{impl}}[0]::drop[0] @@ local_drop_glue[External] fn drop(&mut self) {} } -//~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] +//~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue[Internal] struct Outer { _a: Struct } -//~ TRANS_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] +//~ MONO_ITEM fn local_drop_glue::user[0] @@ local_drop_glue[External] pub fn user() { let _ = Outer { @@ -46,14 +46,14 @@ pub mod mod1 { use super::Struct; - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ local_drop_glue-mod1[Internal] struct Struct2 { _a: Struct, - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0]<(u32, local_drop_glue::Struct[0])> @@ local_drop_glue-mod1[Internal] _b: (u32, Struct), } - //~ TRANS_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] + //~ MONO_ITEM fn local_drop_glue::mod1[0]::user[0] @@ local_drop_glue-mod1[External] pub fn user() { let _ = Struct2 { diff --git a/src/test/codegen-units/partitioning/local-generic.rs b/src/test/codegen-units/partitioning/local-generic.rs index 33e3745502f..7c8ca20e1e3 100644 --- a/src/test/codegen-units/partitioning/local-generic.rs +++ b/src/test/codegen-units/partitioning/local-generic.rs @@ -11,18 +11,18 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/local-generic +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/local-generic #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] -//~ TRANS_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0] @@ local_generic.volatile[External] +//~ MONO_ITEM fn local_generic::generic[0]<&str> @@ local_generic.volatile[External] pub fn generic(x: T) -> T { x } -//~ TRANS_ITEM fn local_generic::user[0] @@ local_generic[Internal] +//~ MONO_ITEM fn local_generic::user[0] @@ local_generic[Internal] fn user() { let _ = generic(0u32); } @@ -30,7 +30,7 @@ fn user() { mod mod1 { pub use super::generic; - //~ TRANS_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[Internal] + //~ MONO_ITEM fn local_generic::mod1[0]::user[0] @@ local_generic-mod1[Internal] fn user() { let _ = generic(0u64); } @@ -38,7 +38,7 @@ mod mod1 { mod mod1 { use super::generic; - //~ TRANS_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[Internal] + //~ MONO_ITEM fn local_generic::mod1[0]::mod1[0]::user[0] @@ local_generic-mod1-mod1[Internal] fn user() { let _ = generic('c'); } @@ -48,7 +48,7 @@ mod mod1 { mod mod2 { use super::generic; - //~ TRANS_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[Internal] + //~ MONO_ITEM fn local_generic::mod2[0]::user[0] @@ local_generic-mod2[Internal] fn user() { let _ = generic("abc"); } diff --git a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs index cf197301eec..747f768c11f 100644 --- a/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs +++ b/src/test/codegen-units/partitioning/local-inlining-but-not-all.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining-but-not-all // compile-flags:-Zinline-in-all-cgus=no #![allow(dead_code)] @@ -19,7 +19,7 @@ mod inline { - //~ TRANS_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] + //~ MONO_ITEM fn local_inlining_but_not_all::inline[0]::inlined_function[0] @@ local_inlining_but_not_all-inline[External] #[inline] pub fn inlined_function() { @@ -30,7 +30,7 @@ mod inline { pub mod user1 { use super::inline; - //~ TRANS_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] + //~ MONO_ITEM fn local_inlining_but_not_all::user1[0]::foo[0] @@ local_inlining_but_not_all-user1[External] pub fn foo() { inline::inlined_function(); } @@ -39,7 +39,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ TRANS_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] + //~ MONO_ITEM fn local_inlining_but_not_all::user2[0]::bar[0] @@ local_inlining_but_not_all-user2[External] pub fn bar() { inline::inlined_function(); } @@ -47,7 +47,7 @@ pub mod user2 { pub mod non_user { - //~ TRANS_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] + //~ MONO_ITEM fn local_inlining_but_not_all::non_user[0]::baz[0] @@ local_inlining_but_not_all-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-inlining.rs b/src/test/codegen-units/partitioning/local-inlining.rs index 3502aa59fdc..f144f0d992b 100644 --- a/src/test/codegen-units/partitioning/local-inlining.rs +++ b/src/test/codegen-units/partitioning/local-inlining.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -20,7 +20,7 @@ mod inline { // Important: This function should show up in all codegen units where it is inlined - //~ TRANS_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-user1[Internal] local_inlining-user2[Internal] + //~ MONO_ITEM fn local_inlining::inline[0]::inlined_function[0] @@ local_inlining-user1[Internal] local_inlining-user2[Internal] #[inline(always)] pub fn inlined_function() { @@ -31,7 +31,7 @@ mod inline { pub mod user1 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] + //~ MONO_ITEM fn local_inlining::user1[0]::foo[0] @@ local_inlining-user1[External] pub fn foo() { inline::inlined_function(); } @@ -40,7 +40,7 @@ pub mod user1 { pub mod user2 { use super::inline; - //~ TRANS_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] + //~ MONO_ITEM fn local_inlining::user2[0]::bar[0] @@ local_inlining-user2[External] pub fn bar() { inline::inlined_function(); } @@ -48,7 +48,7 @@ pub mod user2 { pub mod non_user { - //~ TRANS_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] + //~ MONO_ITEM fn local_inlining::non_user[0]::baz[0] @@ local_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/local-transitive-inlining.rs b/src/test/codegen-units/partitioning/local-transitive-inlining.rs index ed883954f3f..8637844a83d 100644 --- a/src/test/codegen-units/partitioning/local-transitive-inlining.rs +++ b/src/test/codegen-units/partitioning/local-transitive-inlining.rs @@ -11,7 +11,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/local-transitive-inlining // compile-flags:-Zinline-in-all-cgus #![allow(dead_code)] @@ -19,7 +19,7 @@ mod inline { - //~ TRANS_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn local_transitive_inlining::inline[0]::inlined_function[0] @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn inlined_function() { @@ -30,7 +30,7 @@ mod inline { mod direct_user { use super::inline; - //~ TRANS_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-indirect_user[Internal] + //~ MONO_ITEM fn local_transitive_inlining::direct_user[0]::foo[0] @@ local_transitive_inlining-indirect_user[Internal] #[inline(always)] pub fn foo() { inline::inlined_function(); @@ -40,7 +40,7 @@ mod direct_user { pub mod indirect_user { use super::direct_user; - //~ TRANS_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] + //~ MONO_ITEM fn local_transitive_inlining::indirect_user[0]::bar[0] @@ local_transitive_inlining-indirect_user[External] pub fn bar() { direct_user::foo(); } @@ -48,7 +48,7 @@ pub mod indirect_user { pub mod non_user { - //~ TRANS_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] + //~ MONO_ITEM fn local_transitive_inlining::non_user[0]::baz[0] @@ local_transitive_inlining-non_user[External] pub fn baz() { } diff --git a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs index aa01289de59..ff25a7194e0 100644 --- a/src/test/codegen-units/partitioning/methods-are-with-self-type.rs +++ b/src/test/codegen-units/partitioning/methods-are-with-self-type.rs @@ -16,7 +16,7 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/methods-are-with-self-type +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/methods-are-with-self-type #![allow(dead_code)] #![feature(start)] @@ -31,10 +31,10 @@ mod mod1 { // Even though the impl is in `mod1`, the methods should end up in the // parent module, since that is where their self-type is. impl SomeType { - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::method[0] @@ methods_are_with_self_type[External] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::method[0] @@ methods_are_with_self_type[External] fn method(&self) {} - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::associated_fn[0] @@ methods_are_with_self_type[External] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[0]::associated_fn[0] @@ methods_are_with_self_type[External] fn associated_fn() {} } @@ -64,25 +64,25 @@ mod type2 { pub struct Struct; } -//~ TRANS_ITEM fn methods_are_with_self_type::start[0] +//~ MONO_ITEM fn methods_are_with_self_type::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::method[0] @@ methods_are_with_self_type.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::method[0] @@ methods_are_with_self_type.volatile[WeakODR] SomeGenericType(0u32, 0u64).method(); - //~ TRANS_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::associated_fn[0] @@ methods_are_with_self_type.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::mod1[0]::{{impl}}[1]::associated_fn[0] @@ methods_are_with_self_type.volatile[WeakODR] SomeGenericType::associated_fn('c', "&str"); - //~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] type1::Struct.foo(); - //~ TRANS_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::{{impl}}[0]::foo[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] type2::Struct.foo(); - //~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type1.volatile[WeakODR] type1::Struct.default(); - //~ TRANS_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] + //~ MONO_ITEM fn methods_are_with_self_type::Trait[0]::default[0] @@ methods_are_with_self_type-type2.volatile[WeakODR] type2::Struct.default(); 0 } -//~ TRANS_ITEM drop-glue i8 +//~ MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/regular-modules.rs b/src/test/codegen-units/partitioning/regular-modules.rs index 9bdbc8b85e8..e1ec6f51b30 100644 --- a/src/test/codegen-units/partitioning/regular-modules.rs +++ b/src/test/codegen-units/partitioning/regular-modules.rs @@ -11,72 +11,72 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=eager -Zincremental=tmp/partitioning-tests/regular-modules +// compile-flags:-Zprint-mono-items=eager -Zincremental=tmp/partitioning-tests/regular-modules #![allow(dead_code)] #![crate_type="lib"] -//~ TRANS_ITEM fn regular_modules::foo[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn regular_modules::foo[0] @@ regular_modules[Internal] fn foo() {} -//~ TRANS_ITEM fn regular_modules::bar[0] @@ regular_modules[Internal] +//~ MONO_ITEM fn regular_modules::bar[0] @@ regular_modules[Internal] fn bar() {} -//~ TRANS_ITEM static regular_modules::BAZ[0] @@ regular_modules[Internal] +//~ MONO_ITEM static regular_modules::BAZ[0] @@ regular_modules[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::foo[0] @@ regular_modules-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::bar[0] @@ regular_modules-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::BAZ[0] @@ regular_modules-mod1[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::foo[0] @@ regular_modules-mod1-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod1[0]::bar[0] @@ regular_modules-mod1-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::mod1[0]::BAZ[0] @@ regular_modules-mod1-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::foo[0] @@ regular_modules-mod1-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod1[0]::mod2[0]::bar[0] @@ regular_modules-mod1-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod1[0]::mod2[0]::BAZ[0] @@ regular_modules-mod1-mod2[Internal] static BAZ: u64 = 0; } } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::foo[0] @@ regular_modules-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::bar[0] @@ regular_modules-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::BAZ[0] @@ regular_modules-mod2[Internal] static BAZ: u64 = 0; mod mod1 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::foo[0] @@ regular_modules-mod2-mod1[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod1[0]::bar[0] @@ regular_modules-mod2-mod1[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::mod1[0]::BAZ[0] @@ regular_modules-mod2-mod1[Internal] static BAZ: u64 = 0; } mod mod2 { - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::foo[0] @@ regular_modules-mod2-mod2[Internal] fn foo() {} - //~ TRANS_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM fn regular_modules::mod2[0]::mod2[0]::bar[0] @@ regular_modules-mod2-mod2[Internal] fn bar() {} - //~ TRANS_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[Internal] + //~ MONO_ITEM static regular_modules::mod2[0]::mod2[0]::BAZ[0] @@ regular_modules-mod2-mod2[Internal] static BAZ: u64 = 0; } } diff --git a/src/test/codegen-units/partitioning/shared-generics.rs b/src/test/codegen-units/partitioning/shared-generics.rs index d352609bfcb..880361fac2e 100644 --- a/src/test/codegen-units/partitioning/shared-generics.rs +++ b/src/test/codegen-units/partitioning/shared-generics.rs @@ -9,17 +9,17 @@ // except according to those terms. // ignore-tidy-linelength -// compile-flags:-Zprint-trans-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe +// compile-flags:-Zprint-mono-items=eager -Zshare-generics=yes -Zincremental=tmp/partitioning-tests/shared-generics-exe #![crate_type="rlib"] // aux-build:shared_generics_aux.rs extern crate shared_generics_aux; -//~ TRANS_ITEM fn shared_generics::foo[0] +//~ MONO_ITEM fn shared_generics::foo[0] pub fn foo() { - //~ TRANS_ITEM fn shared_generics_aux::generic_fn[0] @@ shared_generics_aux.volatile[External] + //~ MONO_ITEM fn shared_generics_aux::generic_fn[0] @@ shared_generics_aux.volatile[External] let _ = shared_generics_aux::generic_fn(0u16, 1u16); // This should not generate a monomorphization because it's already @@ -27,4 +27,4 @@ pub fn foo() { let _ = shared_generics_aux::generic_fn(0.0f32, 3.0f32); } -// TRANS_ITEM drop-glue i8 +// MONO_ITEM drop-glue i8 diff --git a/src/test/codegen-units/partitioning/statics.rs b/src/test/codegen-units/partitioning/statics.rs index 12ef34441ff..b6c1e5210da 100644 --- a/src/test/codegen-units/partitioning/statics.rs +++ b/src/test/codegen-units/partitioning/statics.rs @@ -11,38 +11,38 @@ // ignore-tidy-linelength // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/statics +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/statics #![crate_type="rlib"] -//~ TRANS_ITEM static statics::FOO[0] @@ statics[Internal] +//~ MONO_ITEM static statics::FOO[0] @@ statics[Internal] static FOO: u32 = 0; -//~ TRANS_ITEM static statics::BAR[0] @@ statics[Internal] +//~ MONO_ITEM static statics::BAR[0] @@ statics[Internal] static BAR: u32 = 0; -//~ TRANS_ITEM fn statics::function[0] @@ statics[External] +//~ MONO_ITEM fn statics::function[0] @@ statics[External] pub fn function() { - //~ TRANS_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] + //~ MONO_ITEM static statics::function[0]::FOO[0] @@ statics[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::function[0]::BAR[0] @@ statics[Internal] + //~ MONO_ITEM static statics::function[0]::BAR[0] @@ statics[Internal] static BAR: u32 = 0; } pub mod mod1 { - //~ TRANS_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::BAR[0] @@ statics-mod1[Internal] static BAR: u32 = 0; - //~ TRANS_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] + //~ MONO_ITEM fn statics::mod1[0]::function[0] @@ statics-mod1[External] pub fn function() { - //~ TRANS_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::function[0]::FOO[0] @@ statics-mod1[Internal] static FOO: u32 = 0; - //~ TRANS_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[Internal] + //~ MONO_ITEM static statics::mod1[0]::function[0]::BAR[0] @@ statics-mod1[Internal] static BAR: u32 = 0; } } diff --git a/src/test/codegen-units/partitioning/vtable-through-const.rs b/src/test/codegen-units/partitioning/vtable-through-const.rs index d0acddda637..74533c1015b 100644 --- a/src/test/codegen-units/partitioning/vtable-through-const.rs +++ b/src/test/codegen-units/partitioning/vtable-through-const.rs @@ -12,7 +12,7 @@ // We specify -Z incremental here because we want to test the partitioning for // incremental compilation -// compile-flags:-Zprint-trans-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const +// compile-flags:-Zprint-mono-items=lazy -Zincremental=tmp/partitioning-tests/vtable-through-const // compile-flags:-Zinline-in-all-cgus // This test case makes sure, that references made through constants are @@ -40,7 +40,7 @@ mod mod1 { fn id(x: T) -> T { x } - // These are referenced, so they produce trans-items (see start()) + // These are referenced, so they produce mono-items (see start()) pub const TRAIT1_REF: &'static Trait1 = &0u32 as &Trait1; pub const TRAIT1_GEN_REF: &'static Trait1Gen = &0u32 as &Trait1Gen; pub const ID_CHAR: fn(char) -> char = id::; @@ -64,34 +64,34 @@ mod mod1 { fn do_something_else(&self, x: T) -> T { x } } - // These are not referenced, so they do not produce trans-items + // These are not referenced, so they do not produce mono-items pub const TRAIT2_REF: &'static Trait2 = &0u32 as &Trait2; pub const TRAIT2_GEN_REF: &'static Trait2Gen = &0u32 as &Trait2Gen; pub const ID_I64: fn(i64) -> i64 = id::; } -//~ TRANS_ITEM fn vtable_through_const::start[0] +//~ MONO_ITEM fn vtable_through_const::start[0] #[start] fn start(_: isize, _: *const *const u8) -> isize { - //~ TRANS_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] + //~ MONO_ITEM fn core::ptr[0]::drop_in_place[0] @@ vtable_through_const[Internal] // Since Trait1::do_something() is instantiated via its default implementation, // it is considered a generic and is instantiated here only because it is // referenced in this module. - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] // Although it is never used, Trait1::do_something_else() has to be // instantiated locally here too, otherwise the <&u32 as &Trait1> vtable // could not be fully constructed. - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::Trait1[0]::do_something[0] @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_REF.do_something(); // Same as above - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const-mod1.volatile[External] - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::{{impl}}[1]::do_something_else[0] @@ vtable_through_const-mod1.volatile[External] mod1::TRAIT1_GEN_REF.do_something(0u8); - //~ TRANS_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[External] + //~ MONO_ITEM fn vtable_through_const::mod1[0]::id[0] @@ vtable_through_const-mod1.volatile[External] mod1::ID_CHAR('x'); 0 diff --git a/src/test/compile-fail/const-err.rs b/src/test/compile-fail/const-err.rs index 8bd759b6d37..f6a64bcba21 100644 --- a/src/test/compile-fail/const-err.rs +++ b/src/test/compile-fail/const-err.rs @@ -10,7 +10,7 @@ // compile-flags: -Zforce-overflow-checks=on -// these errors are not actually "const_err", they occur in trans/consts +// these errors are not actually "const_err", they occur in codegen/consts // and are unconditional warnings that can't be denied or allowed #![allow(exceeding_bitshifts)] diff --git a/src/test/compile-fail/const-eval-overflow2.rs b/src/test/compile-fail/const-eval-overflow2.rs index faa8c3039b7..7b5db7a4f6d 100644 --- a/src/test/compile-fail/const-eval-overflow2.rs +++ b/src/test/compile-fail/const-eval-overflow2.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/const-eval-overflow2b.rs b/src/test/compile-fail/const-eval-overflow2b.rs index d827e680c5b..ce4dc72555d 100644 --- a/src/test/compile-fail/const-eval-overflow2b.rs +++ b/src/test/compile-fail/const-eval-overflow2b.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/const-eval-overflow2c.rs b/src/test/compile-fail/const-eval-overflow2c.rs index 2fd46b038ef..88eb14a1330 100644 --- a/src/test/compile-fail/const-eval-overflow2c.rs +++ b/src/test/compile-fail/const-eval-overflow2c.rs @@ -11,7 +11,7 @@ #![allow(unused_imports)] // Note: the relevant lint pass here runs before some of the constant -// evaluation below (e.g. that performed by trans and llvm), so if you +// evaluation below (e.g. that performed by codegen and llvm), so if you // change this warn to a deny, then the compiler will exit before // those errors are detected. diff --git a/src/test/compile-fail/dep-graph-assoc-type-codegen.rs b/src/test/compile-fail/dep-graph-assoc-type-codegen.rs new file mode 100644 index 00000000000..c20cfbc7e23 --- /dev/null +++ b/src/test/compile-fail/dep-graph-assoc-type-codegen.rs @@ -0,0 +1,47 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that when a trait impl changes, fns whose body uses that trait +// must also be recompiled. + +// compile-flags: -Z query-dep-graph + +#![feature(rustc_attrs)] +#![allow(warnings)] + +fn main() { } + +pub trait Foo: Sized { + type T; + fn method(self) { } +} + +mod x { + use Foo; + + #[rustc_if_this_changed] + impl Foo for char { type T = char; } + + impl Foo for u32 { type T = u32; } +} + +mod y { + use Foo; + + #[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK + pub fn use_char_assoc() { + // Careful here: in the representation, ::T gets + // normalized away, so at a certain point we had no edge to + // codegen. (But now codegen just depends on typeck.) + let x: ::T = 'a'; + } + + pub fn take_foo(t: T) { } +} diff --git a/src/test/compile-fail/dep-graph-assoc-type-trans.rs b/src/test/compile-fail/dep-graph-assoc-type-trans.rs deleted file mode 100644 index 007a80008a8..00000000000 --- a/src/test/compile-fail/dep-graph-assoc-type-trans.rs +++ /dev/null @@ -1,47 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that when a trait impl changes, fns whose body uses that trait -// must also be recompiled. - -// compile-flags: -Z query-dep-graph - -#![feature(rustc_attrs)] -#![allow(warnings)] - -fn main() { } - -pub trait Foo: Sized { - type T; - fn method(self) { } -} - -mod x { - use Foo; - - #[rustc_if_this_changed] - impl Foo for char { type T = char; } - - impl Foo for u32 { type T = u32; } -} - -mod y { - use Foo; - - #[rustc_then_this_would_need(TypeckTables)] //~ ERROR OK - pub fn use_char_assoc() { - // Careful here: in the representation, ::T gets - // normalized away, so at a certain point we had no edge to - // trans. (But now trans just depends on typeck.) - let x: ::T = 'a'; - } - - pub fn take_foo(t: T) { } -} diff --git a/src/test/debuginfo/struct-with-destructor.rs b/src/test/debuginfo/struct-with-destructor.rs index af70b4a63fd..ab935a07d65 100644 --- a/src/test/debuginfo/struct-with-destructor.rs +++ b/src/test/debuginfo/struct-with-destructor.rs @@ -91,7 +91,7 @@ struct NestedOuter { // The compiler adds a 'destructed' boolean field to structs implementing Drop. This field is used -// at runtime to prevent drop() to be executed more than once (see middle::trans::adt). +// at runtime to prevent drop() to be executed more than once. // This field must be incorporated by the debug info generation. Otherwise the debugger assumes a // wrong size/layout for the struct. fn main() { diff --git a/src/test/debuginfo/var-captured-in-sendable-closure.rs b/src/test/debuginfo/var-captured-in-sendable-closure.rs index 120bbdd7ba9..9aeb3bc9133 100644 --- a/src/test/debuginfo/var-captured-in-sendable-closure.rs +++ b/src/test/debuginfo/var-captured-in-sendable-closure.rs @@ -72,7 +72,7 @@ fn main() { let constant2 = 6_usize; // The `self` argument of the following closure should be passed by value - // to FnOnce::call_once(self, args), which gets translated a bit differently + // to FnOnce::call_once(self, args), which gets codegened a bit differently // than the regular case. Let's make sure this is supported too. let immedate_env = move || { zzz(); // #break diff --git a/src/test/incremental/cache_file_headers.rs b/src/test/incremental/cache_file_headers.rs index feecfecd0b8..02ee06b4cf4 100644 --- a/src/test/incremental/cache_file_headers.rs +++ b/src/test/incremental/cache_file_headers.rs @@ -13,7 +13,7 @@ // different compiler version. This is tested by artificially forcing the // emission of a different compiler version in the header of rpass1 artifacts, // and then making sure that the only object file of the test program gets -// re-translated although the program stays unchanged. +// re-codegened although the program stays unchanged. // The `l33t haxx0r` Rust compiler is known to produce incr. comp. artifacts // that are outrageously incompatible with just about anything, even itself: @@ -23,7 +23,7 @@ // compile-flags: -Z query-dep-graph #![feature(rustc_attrs)] -#![rustc_partition_translated(module="cache_file_headers", cfg="rpass2")] +#![rustc_partition_codegened(module="cache_file_headers", cfg="rpass2")] fn main() { // empty diff --git a/src/test/incremental/change_add_field/struct_point.rs b/src/test/incremental/change_add_field/struct_point.rs index da3b9e4d6d6..37d1a397303 100644 --- a/src/test/incremental/change_add_field/struct_point.rs +++ b/src/test/incremental/change_add_field/struct_point.rs @@ -22,14 +22,14 @@ #![allow(dead_code)] #![crate_type = "rlib"] -// These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_sig", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-call_fn_with_type_in_sig", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_with_type_in_body", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_make_struct", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_read_field", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_write_field", cfg="cfail2")] +// These are expected to require codegen. +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-call_fn_with_type_in_sig", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_with_type_in_body", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_make_struct", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_read_field", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_write_field", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-call_fn_with_type_in_body", cfg="cfail2")] diff --git a/src/test/incremental/change_private_fn/struct_point.rs b/src/test/incremental/change_private_fn/struct_point.rs index 63e137a7e0b..d1b8399dbda 100644 --- a/src/test/incremental/change_private_fn/struct_point.rs +++ b/src/test/incremental/change_private_fn/struct_point.rs @@ -20,7 +20,7 @@ #![allow(dead_code)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] diff --git a/src/test/incremental/change_private_impl_method/struct_point.rs b/src/test/incremental/change_private_impl_method/struct_point.rs index 7f2dd81d0ea..cf6eefd61d7 100644 --- a/src/test/incremental/change_private_impl_method/struct_point.rs +++ b/src/test/incremental/change_private_impl_method/struct_point.rs @@ -20,7 +20,7 @@ #![allow(dead_code)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_same_impl", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_methods_in_another_impl", cfg="cfail2")] diff --git a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs index 412fe72e4e4..a204fe27da6 100644 --- a/src/test/incremental/change_pub_inherent_method_body/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_body/struct_point.rs @@ -19,7 +19,7 @@ #![feature(stmt_expr_attributes)] #![allow(dead_code)] -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_changed_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] diff --git a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs index c82f4645caf..76c9dfce93d 100644 --- a/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs +++ b/src/test/incremental/change_pub_inherent_method_sig/struct_point.rs @@ -19,9 +19,9 @@ #![feature(stmt_expr_attributes)] #![allow(dead_code)] -// These are expected to require translation. -#![rustc_partition_translated(module="struct_point-point", cfg="cfail2")] -#![rustc_partition_translated(module="struct_point-fn_calls_changed_method", cfg="cfail2")] +// These are expected to require codegen. +#![rustc_partition_codegened(module="struct_point-point", cfg="cfail2")] +#![rustc_partition_codegened(module="struct_point-fn_calls_changed_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_calls_another_method", cfg="cfail2")] #![rustc_partition_reused(module="struct_point-fn_make_struct", cfg="cfail2")] diff --git a/src/test/incremental/change_symbol_export_status.rs b/src/test/incremental/change_symbol_export_status.rs index ab91a941a16..feb8026f501 100644 --- a/src/test/incremental/change_symbol_export_status.rs +++ b/src/test/incremental/change_symbol_export_status.rs @@ -14,7 +14,7 @@ #![feature(rustc_attrs)] #![allow(private_no_mangle_fns)] -#![rustc_partition_translated(module="change_symbol_export_status-mod1", cfg="rpass2")] +#![rustc_partition_codegened(module="change_symbol_export_status-mod1", cfg="rpass2")] #![rustc_partition_reused(module="change_symbol_export_status-mod2", cfg="rpass2")] // This test case makes sure that a change in symbol visibility is detected by diff --git a/src/test/incremental/commandline-args.rs b/src/test/incremental/commandline-args.rs index e29f2ec2a13..bb2f385e9c5 100644 --- a/src/test/incremental/commandline-args.rs +++ b/src/test/incremental/commandline-args.rs @@ -16,7 +16,7 @@ #![feature(rustc_attrs)] -#![rustc_partition_translated(module="commandline_args", cfg="rpass2")] +#![rustc_partition_codegened(module="commandline_args", cfg="rpass2")] #![rustc_partition_reused(module="commandline_args", cfg="rpass3")] // Between revisions 1 and 2, we are changing the debuginfo-level, which should diff --git a/src/test/incremental/inlined_hir_34991/main.rs b/src/test/incremental/inlined_hir_34991/main.rs index a150a8c4df7..baf4ae73932 100644 --- a/src/test/incremental/inlined_hir_34991/main.rs +++ b/src/test/incremental/inlined_hir_34991/main.rs @@ -10,7 +10,7 @@ // Regression test for #34991: an ICE occurred here because we inline // some of the vector routines and give them a local def-id `X`. This -// got hashed after trans (`Hir(X)`). When we load back up, we get an +// got hashed after codegen (`Hir(X)`). When we load back up, we get an // error because the `X` is remapped to the original def-id (in // libstd), and we can't hash a HIR node from std. diff --git a/src/test/incremental/issue-38222.rs b/src/test/incremental/issue-38222.rs index f890672aa8f..a19de6ef636 100644 --- a/src/test/incremental/issue-38222.rs +++ b/src/test/incremental/issue-38222.rs @@ -20,7 +20,7 @@ #![rustc_partition_reused(module="issue_38222-mod1", cfg="rpass2")] -// If trans had added a dependency edge to the Krate dep-node, nothing would +// If codegen had added a dependency edge to the Krate dep-node, nothing would // be re-used, so checking that this module was re-used is sufficient. #![rustc_partition_reused(module="issue_38222", cfg="rpass2")] diff --git a/src/test/incremental/issue-49595/issue_49595.rs b/src/test/incremental/issue-49595/issue_49595.rs index a5b0101c68f..dfa92d425f4 100644 --- a/src/test/incremental/issue-49595/issue_49595.rs +++ b/src/test/incremental/issue-49595/issue_49595.rs @@ -15,8 +15,8 @@ #![feature(rustc_attrs)] #![crate_type = "rlib"] -#![rustc_partition_translated(module="issue_49595-tests", cfg="cfail2")] -#![rustc_partition_translated(module="issue_49595-lit_test", cfg="cfail3")] +#![rustc_partition_codegened(module="issue_49595-tests", cfg="cfail2")] +#![rustc_partition_codegened(module="issue_49595-lit_test", cfg="cfail3")] mod tests { #[cfg_attr(not(cfail1), ignore)] diff --git a/src/test/incremental/remapped_paths_cc/main.rs b/src/test/incremental/remapped_paths_cc/main.rs index ce7f5792cea..cd66310dafe 100644 --- a/src/test/incremental/remapped_paths_cc/main.rs +++ b/src/test/incremental/remapped_paths_cc/main.rs @@ -20,7 +20,7 @@ #![rustc_partition_reused(module="main", cfg="rpass2")] #![rustc_partition_reused(module="main-some_mod", cfg="rpass2")] #![rustc_partition_reused(module="main", cfg="rpass3")] -#![rustc_partition_translated(module="main-some_mod", cfg="rpass3")] +#![rustc_partition_codegened(module="main-some_mod", cfg="rpass3")] extern crate extern_crate; diff --git a/src/test/incremental/spike-neg1.rs b/src/test/incremental/spike-neg1.rs index b00c68a184e..d4701b9a66e 100644 --- a/src/test/incremental/spike-neg1.rs +++ b/src/test/incremental/spike-neg1.rs @@ -9,7 +9,7 @@ // except according to those terms. // A variant of the first "spike" test that serves to test the -// `rustc_partition_reused` and `rustc_partition_translated` tests. +// `rustc_partition_reused` and `rustc_partition_codegened` tests. // Here we change and say that the `x` module will be reused (when in // fact it will not), and then indicate that the test itself // should-fail (because an error will be reported, and hence the diff --git a/src/test/incremental/spike-neg2.rs b/src/test/incremental/spike-neg2.rs index 472d11d7f90..da79237b1a6 100644 --- a/src/test/incremental/spike-neg2.rs +++ b/src/test/incremental/spike-neg2.rs @@ -9,8 +9,8 @@ // except according to those terms. // A variant of the first "spike" test that serves to test the -// `rustc_partition_reused` and `rustc_partition_translated` tests. -// Here we change and say that the `y` module will be translated (when +// `rustc_partition_reused` and `rustc_partition_codegened` tests. +// Here we change and say that the `y` module will be codegened (when // in fact it will not), and then indicate that the test itself // should-fail (because an error will be reported, and hence the // revision rpass2 will not compile, despite being named rpass). @@ -21,8 +21,8 @@ #![feature(rustc_attrs)] #![rustc_partition_reused(module="spike_neg2", cfg="rpass2")] -#![rustc_partition_translated(module="spike_neg2-x", cfg="rpass2")] -#![rustc_partition_translated(module="spike_neg2-y", cfg="rpass2")] // this is wrong! +#![rustc_partition_codegened(module="spike_neg2-x", cfg="rpass2")] +#![rustc_partition_codegened(module="spike_neg2-y", cfg="rpass2")] // this is wrong! mod x { pub struct X { diff --git a/src/test/incremental/spike.rs b/src/test/incremental/spike.rs index a820471b7d5..1756511dd37 100644 --- a/src/test/incremental/spike.rs +++ b/src/test/incremental/spike.rs @@ -18,7 +18,7 @@ #![feature(rustc_attrs)] #![rustc_partition_reused(module="spike", cfg="rpass2")] -#![rustc_partition_translated(module="spike-x", cfg="rpass2")] +#![rustc_partition_codegened(module="spike-x", cfg="rpass2")] #![rustc_partition_reused(module="spike-y", cfg="rpass2")] mod x { diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops.rs b/src/test/run-fail/mir_codegen_calls_converging_drops.rs new file mode 100644 index 00000000000..9c851eb7346 --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_converging_drops.rs @@ -0,0 +1,34 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:converging_fn called +// error-pattern:0 dropped +// error-pattern:exit + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("{} dropped", self.0); + } +} + +fn converging_fn() { + eprintln!("converging_fn called"); +} + +fn mir(d: Droppable) { + converging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs b/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs new file mode 100644 index 00000000000..6f105211556 --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_converging_drops_2.rs @@ -0,0 +1,38 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:complex called +// error-pattern:dropped +// error-pattern:exit + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("dropped"); + } +} + +// return value of this function is copied into the return slot +fn complex() -> u64 { + eprintln!("complex called"); + 42 +} + + +fn mir() -> u64 { + let x = Droppable; + return complex(); + drop(x); +} + +pub fn main() { + assert_eq!(mir(), 42); + panic!("exit"); +} diff --git a/src/test/run-fail/mir_codegen_calls_diverging.rs b/src/test/run-fail/mir_codegen_calls_diverging.rs new file mode 100644 index 00000000000..9dbf7de0d2d --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_diverging.rs @@ -0,0 +1,23 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:diverging_fn called + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir() { + diverging_fn(); +} + +fn main() { + mir(); +} diff --git a/src/test/run-fail/mir_codegen_calls_diverging_drops.rs b/src/test/run-fail/mir_codegen_calls_diverging_drops.rs new file mode 100644 index 00000000000..f8fbe8f79cc --- /dev/null +++ b/src/test/run-fail/mir_codegen_calls_diverging_drops.rs @@ -0,0 +1,32 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// error-pattern:diverging_fn called +// error-pattern:0 dropped + +struct Droppable(u8); +impl Drop for Droppable { + fn drop(&mut self) { + eprintln!("{} dropped", self.0); + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir(d: Droppable) { + diverging_fn(); +} + +fn main() { + let d = Droppable(0); + mir(d); +} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads.rs b/src/test/run-fail/mir_codegen_no_landing_pads.rs new file mode 100644 index 00000000000..aded2739b10 --- /dev/null +++ b/src/test/run-fail/mir_codegen_no_landing_pads.rs @@ -0,0 +1,37 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z no-landing-pads -C codegen-units=1 +// error-pattern:converging_fn called +// ignore-cloudabi no std::process + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn converging_fn() { + panic!("converging_fn called") +} + +fn mir(d: Droppable) { + let x = Droppable; + converging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs b/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs new file mode 100644 index 00000000000..d3a8613bbc4 --- /dev/null +++ b/src/test/run-fail/mir_codegen_no_landing_pads_diverging.rs @@ -0,0 +1,37 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// compile-flags: -Z no-landing-pads -C codegen-units=1 +// error-pattern:diverging_fn called +// ignore-cloudabi no std::process + +use std::io::{self, Write}; + +struct Droppable; +impl Drop for Droppable { + fn drop(&mut self) { + ::std::process::exit(1) + } +} + +fn diverging_fn() -> ! { + panic!("diverging_fn called") +} + +fn mir(d: Droppable) { + let x = Droppable; + diverging_fn(); + drop(x); + drop(d); +} + +fn main() { + mir(Droppable); +} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops.rs b/src/test/run-fail/mir_trans_calls_converging_drops.rs deleted file mode 100644 index 9c851eb7346..00000000000 --- a/src/test/run-fail/mir_trans_calls_converging_drops.rs +++ /dev/null @@ -1,34 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:converging_fn called -// error-pattern:0 dropped -// error-pattern:exit - -struct Droppable(u8); -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("{} dropped", self.0); - } -} - -fn converging_fn() { - eprintln!("converging_fn called"); -} - -fn mir(d: Droppable) { - converging_fn(); -} - -fn main() { - let d = Droppable(0); - mir(d); - panic!("exit"); -} diff --git a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs b/src/test/run-fail/mir_trans_calls_converging_drops_2.rs deleted file mode 100644 index 6f105211556..00000000000 --- a/src/test/run-fail/mir_trans_calls_converging_drops_2.rs +++ /dev/null @@ -1,38 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:complex called -// error-pattern:dropped -// error-pattern:exit - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("dropped"); - } -} - -// return value of this function is copied into the return slot -fn complex() -> u64 { - eprintln!("complex called"); - 42 -} - - -fn mir() -> u64 { - let x = Droppable; - return complex(); - drop(x); -} - -pub fn main() { - assert_eq!(mir(), 42); - panic!("exit"); -} diff --git a/src/test/run-fail/mir_trans_calls_diverging.rs b/src/test/run-fail/mir_trans_calls_diverging.rs deleted file mode 100644 index 9dbf7de0d2d..00000000000 --- a/src/test/run-fail/mir_trans_calls_diverging.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:diverging_fn called - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir() { - diverging_fn(); -} - -fn main() { - mir(); -} diff --git a/src/test/run-fail/mir_trans_calls_diverging_drops.rs b/src/test/run-fail/mir_trans_calls_diverging_drops.rs deleted file mode 100644 index f8fbe8f79cc..00000000000 --- a/src/test/run-fail/mir_trans_calls_diverging_drops.rs +++ /dev/null @@ -1,32 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// error-pattern:diverging_fn called -// error-pattern:0 dropped - -struct Droppable(u8); -impl Drop for Droppable { - fn drop(&mut self) { - eprintln!("{} dropped", self.0); - } -} - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir(d: Droppable) { - diverging_fn(); -} - -fn main() { - let d = Droppable(0); - mir(d); -} diff --git a/src/test/run-fail/mir_trans_no_landing_pads.rs b/src/test/run-fail/mir_trans_no_landing_pads.rs deleted file mode 100644 index aded2739b10..00000000000 --- a/src/test/run-fail/mir_trans_no_landing_pads.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:converging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn converging_fn() { - panic!("converging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - converging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs b/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs deleted file mode 100644 index d3a8613bbc4..00000000000 --- a/src/test/run-fail/mir_trans_no_landing_pads_diverging.rs +++ /dev/null @@ -1,37 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// compile-flags: -Z no-landing-pads -C codegen-units=1 -// error-pattern:diverging_fn called -// ignore-cloudabi no std::process - -use std::io::{self, Write}; - -struct Droppable; -impl Drop for Droppable { - fn drop(&mut self) { - ::std::process::exit(1) - } -} - -fn diverging_fn() -> ! { - panic!("diverging_fn called") -} - -fn mir(d: Droppable) { - let x = Droppable; - diverging_fn(); - drop(x); - drop(d); -} - -fn main() { - mir(Droppable); -} diff --git a/src/test/run-fail/rhs-type.rs b/src/test/run-fail/rhs-type.rs index e16ce9c8edb..572b56d02ae 100644 --- a/src/test/run-fail/rhs-type.rs +++ b/src/test/run-fail/rhs-type.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that trans treats the rhs of pth's decl +// Tests that codegen treats the rhs of pth's decl // as a _|_-typed thing, not a str-typed thing // error-pattern:bye diff --git a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs index e266b0f5e83..251fb78a985 100644 --- a/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs +++ b/src/test/run-make-fulldeps/hotplug_codegen_backend/the_backend.rs @@ -12,7 +12,7 @@ extern crate syntax; extern crate rustc; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; use std::any::Any; use std::sync::mpsc; @@ -23,11 +23,11 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc::middle::cstore::MetadataLoader; use rustc::dep_graph::DepGraph; -use rustc_trans_utils::trans_crate::{TransCrate, MetadataOnlyTransCrate}; +use rustc_codegen_utils::codegen_backend::{CodegenBackend, MetadataOnlyCodegenBackend}; -struct TheBackend(Box); +struct TheBackend(Box); -impl TransCrate for TheBackend { +impl CodegenBackend for TheBackend { fn metadata_loader(&self) -> Box { self.0.metadata_loader() } @@ -40,7 +40,7 @@ impl TransCrate for TheBackend { self.0.provide_extern(providers); } - fn trans_crate<'a, 'tcx>( + fn codegen_crate<'a, 'tcx>( &self, tcx: TyCtxt<'a, 'tcx, 'tcx>, _rx: mpsc::Receiver> @@ -50,18 +50,18 @@ impl TransCrate for TheBackend { Box::new(tcx.crate_name(LOCAL_CRATE) as Symbol) } - fn join_trans_and_link( + fn join_codegen_and_link( &self, - trans: Box, + ongoing_codegen: Box, sess: &Session, _dep_graph: &DepGraph, outputs: &OutputFilenames, ) -> Result<(), CompileIncomplete> { use std::io::Write; use rustc::session::config::CrateType; - use rustc_trans_utils::link::out_filename; - let crate_name = trans.downcast::() - .expect("in join_trans_and_link: trans is not a Symbol"); + use rustc_codegen_utils::link::out_filename; + let crate_name = ongoing_codegen.downcast::() + .expect("in join_codegen_and_link: ongoing_codegen is not a Symbol"); for &crate_type in sess.opts.crate_types.iter() { if crate_type != CrateType::CrateTypeExecutable { sess.fatal(&format!("Crate type is {:?}", crate_type)); @@ -75,8 +75,8 @@ impl TransCrate for TheBackend { } } -/// This is the entrypoint for a hot plugged rustc_trans +/// This is the entrypoint for a hot plugged rustc_codegen_llvm #[no_mangle] -pub fn __rustc_codegen_backend() -> Box { - Box::new(TheBackend(MetadataOnlyTransCrate::new())) +pub fn __rustc_codegen_backend() -> Box { + Box::new(TheBackend(MetadataOnlyCodegenBackend::new())) } diff --git a/src/test/run-make-fulldeps/issue-19371/foo.rs b/src/test/run-make-fulldeps/issue-19371/foo.rs index e0db2627d85..403f4f79843 100644 --- a/src/test/run-make-fulldeps/issue-19371/foo.rs +++ b/src/test/run-make-fulldeps/issue-19371/foo.rs @@ -15,7 +15,7 @@ extern crate rustc_driver; extern crate rustc_lint; extern crate rustc_metadata; extern crate rustc_errors; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate syntax; use rustc::session::{build_session, Session}; @@ -25,7 +25,7 @@ use rustc_driver::driver::{compile_input, CompileController}; use rustc_metadata::cstore::CStore; use rustc_errors::registry::Registry; use syntax::codemap::FileName; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use std::path::PathBuf; use std::rc::Rc; @@ -52,7 +52,7 @@ fn main() { compile(src.to_string(), tmpdir.join("out"), sysroot.clone()); } -fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { +fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { let mut opts = basic_options(); opts.output_types = OutputTypes::new(&[(OutputType::Exe, None)]); opts.maybe_sysroot = Some(sysroot); @@ -62,19 +62,19 @@ fn basic_sess(sysroot: PathBuf) -> (Session, Rc, Box) { let descriptions = Registry::new(&rustc::DIAGNOSTICS); let sess = build_session(opts, None, descriptions); - let trans = rustc_driver::get_trans(&sess); - let cstore = Rc::new(CStore::new(trans.metadata_loader())); + let codegen_backend = rustc_driver::get_codegen_backend(&sess); + let cstore = Rc::new(CStore::new(codegen_backend.metadata_loader())); rustc_lint::register_builtins(&mut sess.lint_store.borrow_mut(), Some(&sess)); - (sess, cstore, trans) + (sess, cstore, codegen_backend) } fn compile(code: String, output: PathBuf, sysroot: PathBuf) { syntax::with_globals(|| { - let (sess, cstore, trans) = basic_sess(sysroot); + let (sess, cstore, codegen_backend) = basic_sess(sysroot); let control = CompileController::basic(); let input = Input::Str { name: FileName::Anon, input: code }; let _ = compile_input( - trans, + codegen_backend, &sess, &cstore, &None, diff --git a/src/test/run-make-fulldeps/issue-7349/Makefile b/src/test/run-make-fulldeps/issue-7349/Makefile index 50dc63b1deb..9658b99e3b0 100644 --- a/src/test/run-make-fulldeps/issue-7349/Makefile +++ b/src/test/run-make-fulldeps/issue-7349/Makefile @@ -1,7 +1,7 @@ -include ../tools.mk # Test to make sure that inner functions within a polymorphic outer function -# don't get re-translated when the outer function is monomorphized. The test +# don't get re-codegened when the outer function is monomorphized. The test # code monomorphizes the outer functions several times, but the magic constants # used in the inner functions should each appear only once in the generated IR. diff --git a/src/test/run-pass-fulldeps/compiler-calls.rs b/src/test/run-pass-fulldeps/compiler-calls.rs index 85aa92ce260..9aa4f42c8ac 100644 --- a/src/test/run-pass-fulldeps/compiler-calls.rs +++ b/src/test/run-pass-fulldeps/compiler-calls.rs @@ -18,7 +18,7 @@ extern crate getopts; extern crate rustc; extern crate rustc_driver; -extern crate rustc_trans_utils; +extern crate rustc_codegen_utils; extern crate syntax; extern crate rustc_errors as errors; @@ -26,7 +26,7 @@ use rustc::middle::cstore::CrateStore; use rustc::session::Session; use rustc::session::config::{self, Input}; use rustc_driver::{driver, CompilerCalls, Compilation}; -use rustc_trans_utils::trans_crate::TransCrate; +use rustc_codegen_utils::codegen_backend::CodegenBackend; use syntax::ast; use std::path::PathBuf; @@ -48,7 +48,7 @@ impl<'a> CompilerCalls<'a> for TestCalls { } fn late_callback(&mut self, - _: &TransCrate, + _: &CodegenBackend, _: &getopts::Matches, _: &Session, _: &CrateStore, diff --git a/src/test/run-pass/associated-types-region-erasure-issue-20582.rs b/src/test/run-pass/associated-types-region-erasure-issue-20582.rs index 16e49f146ab..40f352e2e1f 100644 --- a/src/test/run-pass/associated-types-region-erasure-issue-20582.rs +++ b/src/test/run-pass/associated-types-region-erasure-issue-20582.rs @@ -9,7 +9,7 @@ // except according to those terms. // Regression test for #20582. This test caused an ICE related to -// inconsistent region erasure in trans. +// inconsistent region erasure in codegen. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/atomic-compare_exchange.rs b/src/test/run-pass/atomic-compare_exchange.rs index 1d9fa248e3d..2f33eb9ca40 100644 --- a/src/test/run-pass/atomic-compare_exchange.rs +++ b/src/test/run-pass/atomic-compare_exchange.rs @@ -15,7 +15,7 @@ use std::sync::atomic::Ordering::*; static ATOMIC: AtomicIsize = ATOMIC_ISIZE_INIT; fn main() { - // Make sure trans can emit all the intrinsics correctly + // Make sure codegen can emit all the intrinsics correctly ATOMIC.compare_exchange(0, 1, Relaxed, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, Acquire, Relaxed).ok(); ATOMIC.compare_exchange(0, 1, Release, Relaxed).ok(); diff --git a/src/test/run-pass/codegen-object-shim.rs b/src/test/run-pass/codegen-object-shim.rs new file mode 100644 index 00000000000..5fbfef05e10 --- /dev/null +++ b/src/test/run-pass/codegen-object-shim.rs @@ -0,0 +1,14 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn main() { + assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"), + String::from("foo")); +} diff --git a/src/test/run-pass/codegen-tag-static-padding.rs b/src/test/run-pass/codegen-tag-static-padding.rs new file mode 100644 index 00000000000..ba01d51dc6a --- /dev/null +++ b/src/test/run-pass/codegen-tag-static-padding.rs @@ -0,0 +1,67 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + + +// Issue #13186 + +// For simplicity of explanations assuming code is compiled for x86_64 +// Linux ABI. + +// Size of TestOption is 16, and alignment of TestOption is 8. +// Size of u8 is 1, and alignment of u8 is 1. +// So size of Request is 24, and alignment of Request must be 8: +// the maximum alignment of its fields. +// Last 7 bytes of Request struct are not occupied by any fields. + + + +enum TestOption { + TestNone, + TestSome(T), +} + +pub struct Request { + foo: TestOption, + bar: u8, +} + +fn default_instance() -> &'static Request { + static instance: Request = Request { + // LLVM does not allow to specify alignment of expressions, thus + // alignment of `foo` in constant is 1, not 8. + foo: TestOption::TestNone, + bar: 17, + // Space after last field is not occupied by any data, but it is + // reserved to make struct aligned properly. If compiler does + // not insert padding after last field when emitting constant, + // size of struct may be not equal to size of struct, and + // compiler crashes in internal assertion check. + }; + &instance +} + +fn non_default_instance() -> &'static Request { + static instance: Request = Request { + foo: TestOption::TestSome(0x1020304050607080), + bar: 19, + }; + &instance +} + +pub fn main() { + match default_instance() { + &Request { foo: TestOption::TestNone, bar: 17 } => {}, + _ => panic!(), + }; + match non_default_instance() { + &Request { foo: TestOption::TestSome(0x1020304050607080), bar: 19 } => {}, + _ => panic!(), + }; +} diff --git a/src/test/run-pass/compiletest-skip-codegen.rs b/src/test/run-pass/compiletest-skip-codegen.rs new file mode 100644 index 00000000000..d318b8fa44b --- /dev/null +++ b/src/test/run-pass/compiletest-skip-codegen.rs @@ -0,0 +1,17 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// Test that with the `skip-codegen` option the test isn't executed. + +// skip-codegen + +fn main() { + unreachable!(); +} diff --git a/src/test/run-pass/compiletest-skip-trans.rs b/src/test/run-pass/compiletest-skip-trans.rs deleted file mode 100644 index d24a6506c2c..00000000000 --- a/src/test/run-pass/compiletest-skip-trans.rs +++ /dev/null @@ -1,17 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// Test that with the `skip-trans` option the test isn't executed. - -// skip-trans - -fn main() { - unreachable!(); -} diff --git a/src/test/run-pass/conditional-compile.rs b/src/test/run-pass/conditional-compile.rs index c8e9cbdae1e..01bdcfeaefb 100644 --- a/src/test/run-pass/conditional-compile.rs +++ b/src/test/run-pass/conditional-compile.rs @@ -22,7 +22,7 @@ mod rustrt { #[cfg(bogus)] extern { // This symbol doesn't exist and would be a link error if this - // module was translated + // module was codegened pub fn bogus(); } diff --git a/src/test/run-pass/issue-18425.rs b/src/test/run-pass/issue-18425.rs index eb7e504ae14..797b3197182 100644 --- a/src/test/run-pass/issue-18425.rs +++ b/src/test/run-pass/issue-18425.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that trans doesn't ICE when translating an array repeat +// Check that codegen doesn't ICE when codegenning an array repeat // expression with a count of 1 and a non-Copy element type. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue-18514.rs b/src/test/run-pass/issue-18514.rs index 2a1e55d867f..f8bebb4a40b 100644 --- a/src/test/run-pass/issue-18514.rs +++ b/src/test/run-pass/issue-18514.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Test that we don't ICE when translating a generic impl method from +// Test that we don't ICE when codegenning a generic impl method from // an extern crate that contains a match expression on a local // variable place where one of the match case bodies contains an // expression that autoderefs through an overloaded generic deref diff --git a/src/test/run-pass/issue-18652.rs b/src/test/run-pass/issue-18652.rs index cea0beaf5f0..1eddc34f5b1 100644 --- a/src/test/run-pass/issue-18652.rs +++ b/src/test/run-pass/issue-18652.rs @@ -9,7 +9,7 @@ // except according to those terms. // Tests multiple free variables being passed by value into an unboxed -// once closure as an optimization by trans. This used to hit an +// once closure as an optimization by codegen. This used to hit an // incorrect assert. fn main() { diff --git a/src/test/run-pass/issue-18661.rs b/src/test/run-pass/issue-18661.rs index 48d29095ec6..4287f449c6f 100644 --- a/src/test/run-pass/issue-18661.rs +++ b/src/test/run-pass/issue-18661.rs @@ -9,7 +9,7 @@ // except according to those terms. // Test that param substitutions from the correct environment are -// used when translating unboxed closure calls. +// used when codegenning unboxed closure calls. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue-20644.rs b/src/test/run-pass/issue-20644.rs index 7cacc2e1146..65a67d0b41a 100644 --- a/src/test/run-pass/issue-20644.rs +++ b/src/test/run-pass/issue-20644.rs @@ -9,7 +9,7 @@ // except according to those terms. // A reduced version of the rustbook ice. The problem this encountered -// had to do with trans ignoring binders. +// had to do with codegen ignoring binders. // pretty-expanded FIXME #23616 // ignore-cloudabi no std::fs diff --git a/src/test/run-pass/issue-24085.rs b/src/test/run-pass/issue-24085.rs index fde32cb9023..b15ec2986c2 100644 --- a/src/test/run-pass/issue-24085.rs +++ b/src/test/run-pass/issue-24085.rs @@ -10,7 +10,7 @@ // Regression test for #24085. Errors were occurring in region // inference due to the requirement that `'a:b'`, which was getting -// incorrectly translated in connection with the closure below. +// incorrectly codegened in connection with the closure below. #[derive(Copy,Clone)] struct Path<'a:'b, 'b> { diff --git a/src/test/run-pass/issue-34569.rs b/src/test/run-pass/issue-34569.rs index 41d02e96cc2..5c7c5a2b3b9 100644 --- a/src/test/run-pass/issue-34569.rs +++ b/src/test/run-pass/issue-34569.rs @@ -12,7 +12,7 @@ // In this test we just want to make sure that the code below does not lead to // a debuginfo verification assertion during compilation. This was caused by the -// closure in the guard being translated twice due to how match expressions are +// closure in the guard being codegened twice due to how match expressions are // handled. // // See https://github.com/rust-lang/rust/issues/34569 for details. diff --git a/src/test/run-pass/issue-36381.rs b/src/test/run-pass/issue-36381.rs index 6cd991bd942..2694c98dd91 100644 --- a/src/test/run-pass/issue-36381.rs +++ b/src/test/run-pass/issue-36381.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Regression test for #36381. The trans collector was asserting that +// Regression test for #36381. The monomorphization collector was asserting that // there are no projection types, but the `<&str as // StreamOnce>::Position` projection contained a late-bound region, // and we don't currently normalize in that case until the function is diff --git a/src/test/run-pass/issue-38002.rs b/src/test/run-pass/issue-38002.rs index dd6ccec973f..4eb381b9eac 100644 --- a/src/test/run-pass/issue-38002.rs +++ b/src/test/run-pass/issue-38002.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Check that constant ADTs are translated OK, part k of N. +// Check that constant ADTs are codegened OK, part k of N. enum Bar { C diff --git a/src/test/run-pass/issue-5243.rs b/src/test/run-pass/issue-5243.rs index eda0eea7071..9bd13c4d3a0 100644 --- a/src/test/run-pass/issue-5243.rs +++ b/src/test/run-pass/issue-5243.rs @@ -9,7 +9,7 @@ // except according to those terms. // Check that merely having lifetime parameters is not -// enough for trans to consider this as non-monomorphic, +// enough for codegen to consider this as non-monomorphic, // which led to various assertions and failures in turn. // pretty-expanded FIXME #23616 diff --git a/src/test/run-pass/issue24687-embed-debuginfo/main.rs b/src/test/run-pass/issue24687-embed-debuginfo/main.rs index 7754e9c3ad7..abec252c74e 100644 --- a/src/test/run-pass/issue24687-embed-debuginfo/main.rs +++ b/src/test/run-pass/issue24687-embed-debuginfo/main.rs @@ -14,7 +14,7 @@ extern crate issue24687_lib as d; fn main() { - // Create a d, which has a destructor whose body will be trans'ed + // Create a d, which has a destructor whose body will be codegen'ed // into the generated code here, and thus the local debuginfo will // need references into the original source locations from // `importer` above. diff --git a/src/test/run-pass/method-two-trait-defer-resolution-2.rs b/src/test/run-pass/method-two-trait-defer-resolution-2.rs index cf9bc9bb56a..82d747b6c27 100644 --- a/src/test/run-pass/method-two-trait-defer-resolution-2.rs +++ b/src/test/run-pass/method-two-trait-defer-resolution-2.rs @@ -16,7 +16,7 @@ // whether `_1: MyCopy` or `_1 == Box`. However (and this is the // point of the test), we don't have to pick between the two impls -- // it is enough to know that `foo` comes from the `Foo` trait. We can -// translate the call as `Foo::foo(&x)` and let the specific impl get +// codegen the call as `Foo::foo(&x)` and let the specific impl get // chosen later. diff --git a/src/test/run-pass/mir_codegen_array.rs b/src/test/run-pass/mir_codegen_array.rs new file mode 100644 index 00000000000..b7f247012ce --- /dev/null +++ b/src/test/run-pass/mir_codegen_array.rs @@ -0,0 +1,19 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn into_inner() -> [u64; 1024] { + let mut x = 10 + 20; + [x; 1024] +} + +fn main(){ + let x: &[u64] = &[30; 1024]; + assert_eq!(&into_inner()[..], x); +} diff --git a/src/test/run-pass/mir_codegen_array_2.rs b/src/test/run-pass/mir_codegen_array_2.rs new file mode 100644 index 00000000000..c7133fb0c0e --- /dev/null +++ b/src/test/run-pass/mir_codegen_array_2.rs @@ -0,0 +1,18 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn into_inner(x: u64) -> [u64; 1024] { + [x; 2*4*8*16] +} + +fn main(){ + let x: &[u64] = &[42; 1024]; + assert_eq!(&into_inner(42)[..], x); +} diff --git a/src/test/run-pass/mir_codegen_call_converging.rs b/src/test/run-pass/mir_codegen_call_converging.rs new file mode 100644 index 00000000000..7d420bb86c6 --- /dev/null +++ b/src/test/run-pass/mir_codegen_call_converging.rs @@ -0,0 +1,26 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +fn converging_fn() -> u64 { + 43 +} + +fn mir() -> u64 { + let x; + loop { + x = converging_fn(); + break; + } + x +} + +fn main() { + assert_eq!(mir(), 43); +} diff --git a/src/test/run-pass/mir_codegen_calls.rs b/src/test/run-pass/mir_codegen_calls.rs new file mode 100644 index 00000000000..d02e3287bc3 --- /dev/null +++ b/src/test/run-pass/mir_codegen_calls.rs @@ -0,0 +1,200 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(fn_traits, test)] + +extern crate test; + +fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { + // Test passing a number of arguments including a fat pointer. + // Also returning via an out pointer + fn callee(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { + (a, b, c) + } + callee(a, b, c) +} + +fn test2(a: isize) -> isize { + // Test passing a single argument. + // Not using out pointer. + fn callee(a: isize) -> isize { + a + } + callee(a) +} + +#[derive(PartialEq, Eq, Debug)] +struct Foo; +impl Foo { + fn inherent_method(&self, a: isize) -> isize { a } +} + +fn test3(x: &Foo, a: isize) -> isize { + // Test calling inherent method + x.inherent_method(a) +} + +trait Bar { + fn extension_method(&self, a: isize) -> isize { a } +} +impl Bar for Foo {} + +fn test4(x: &Foo, a: isize) -> isize { + // Test calling extension method + x.extension_method(a) +} + +fn test5(x: &Bar, a: isize) -> isize { + // Test calling method on trait object + x.extension_method(a) +} + +fn test6(x: &T, a: isize) -> isize { + // Test calling extension method on generic callee + x.extension_method(a) +} + +trait One { + fn one() -> T; +} +impl One for isize { + fn one() -> isize { 1 } +} + +fn test7() -> isize { + // Test calling trait static method + ::one() +} + +struct Two; +impl Two { + fn two() -> isize { 2 } +} + +fn test8() -> isize { + // Test calling impl static method + Two::two() +} + +extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 { + x + y.0 * y.1 +} + +fn test9() -> u32 { + simple_extern(41, (42, 43)) +} + +fn test_closure(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f(x, y) +} + +fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + f(x, y) +} + +fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { + // This call goes through the Fn implementation for &Fn provided in + // core::ops::impls. It expands to a static Fn::call() that calls the + // Fn::call() implementation of the object shim underneath. + f(x, y) +} + +fn test_fn_direct_call(f: &F, x: i32, y: i32) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((x, y)) +} + +fn test_fn_const_call(f: &F) -> i32 + where F: Fn(i32, i32) -> i32 +{ + f.call((100, -1)) +} + +fn test_fn_nil_call(f: &F) -> i32 + where F: Fn() -> i32 +{ + f() +} + +fn test_fn_transmute_zst(x: ()) -> [(); 1] { + fn id(x: T) -> T {x} + + id(unsafe { + std::mem::transmute(x) + }) +} + +fn test_fn_ignored_pair() -> ((), ()) { + ((), ()) +} + +fn test_fn_ignored_pair_0() { + test_fn_ignored_pair().0 +} + +fn id(x: T) -> T { x } + +fn ignored_pair_named() -> (Foo, Foo) { + (Foo, Foo) +} + +fn test_fn_ignored_pair_named() -> (Foo, Foo) { + id(ignored_pair_named()) +} + +fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) { + let y = *x; + let z = y.0; + (z.0, z.1) +} + +fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 { + // Mutate the by-reference argument, which won't work with + // a non-immediate constant unless it's copied to the stack. + let a = test::black_box(&mut a); + a[0] += a[1]; + a[0] += a[2]; + a[0] += a[3]; + a[0] +} + +fn main() { + assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); + assert_eq!(test2(98), 98); + assert_eq!(test3(&Foo, 42), 42); + assert_eq!(test4(&Foo, 970), 970); + assert_eq!(test5(&Foo, 8576), 8576); + assert_eq!(test6(&Foo, 12367), 12367); + assert_eq!(test7(), 1); + assert_eq!(test8(), 2); + assert_eq!(test9(), 41 + 42 * 43); + + let r = 3; + let closure = |x: i32, y: i32| { r*(x + (y*2)) }; + assert_eq!(test_fn_const_call(&closure), 294); + assert_eq!(test_closure(&closure, 100, 1), 306); + let function_object = &closure as &Fn(i32, i32) -> i32; + assert_eq!(test_fn_object(function_object, 100, 2), 312); + assert_eq!(test_fn_impl(&function_object, 100, 3), 318); + assert_eq!(test_fn_direct_call(&closure, 100, 4), 324); + + assert_eq!(test_fn_nil_call(&(|| 42)), 42); + assert_eq!(test_fn_transmute_zst(()), [()]); + + assert_eq!(test_fn_ignored_pair_0(), ()); + assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo)); + assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0)); + + const ARRAY: [u64; 4] = [1, 2, 3, 4]; + assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4); +} diff --git a/src/test/run-pass/mir_codegen_calls_variadic.rs b/src/test/run-pass/mir_codegen_calls_variadic.rs new file mode 100644 index 00000000000..7845c9426e2 --- /dev/null +++ b/src/test/run-pass/mir_codegen_calls_variadic.rs @@ -0,0 +1,31 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// ignore-wasm32-bare no libc to test ffi with + +#[link(name = "rust_test_helpers", kind = "static")] +extern { + fn rust_interesting_average(_: i64, ...) -> f64; +} + +fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 { + unsafe { + rust_interesting_average(6, a, a as f64, + b, b as f64, + c, c as f64, + d, d as f64, + e, e as f64, + f, g) as i64 + } +} + +fn main(){ + assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70); +} diff --git a/src/test/run-pass/mir_codegen_critical_edge.rs b/src/test/run-pass/mir_codegen_critical_edge.rs new file mode 100644 index 00000000000..c742e71633f --- /dev/null +++ b/src/test/run-pass/mir_codegen_critical_edge.rs @@ -0,0 +1,52 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This code produces a CFG with critical edges that, if we don't +// handle properly, will cause invalid codegen. + +#![feature(rustc_attrs)] + +enum State { + Both, + Front, + Back +} + +pub struct Foo { + state: State, + a: A, + b: B +} + +impl Foo +where A: Iterator, B: Iterator +{ + // This is the function we care about + fn next(&mut self) -> Option { + match self.state { + State::Both => match self.a.next() { + elt @ Some(..) => elt, + None => { + self.state = State::Back; + self.b.next() + } + }, + State::Front => self.a.next(), + State::Back => self.b.next(), + } + } +} + +// Make sure we actually codegen a version of the function +pub fn do_stuff(mut f: Foo>, Box>>) { + let _x = f.next(); +} + +fn main() {} diff --git a/src/test/run-pass/mir_codegen_spike1.rs b/src/test/run-pass/mir_codegen_spike1.rs new file mode 100644 index 00000000000..27e1583af34 --- /dev/null +++ b/src/test/run-pass/mir_codegen_spike1.rs @@ -0,0 +1,21 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// A simple spike test for MIR version of codegen. + +fn sum(x: i32, y: i32) -> i32 { + x + y +} + +fn main() { + let x = sum(22, 44); + assert_eq!(x, 66); + println!("sum()={:?}", x); +} diff --git a/src/test/run-pass/mir_codegen_switch.rs b/src/test/run-pass/mir_codegen_switch.rs new file mode 100644 index 00000000000..b097bf46ad3 --- /dev/null +++ b/src/test/run-pass/mir_codegen_switch.rs @@ -0,0 +1,44 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +enum Abc { + A(u8), + B(i8), + C, + D, +} + +fn foo(x: Abc) -> i32 { + match x { + Abc::C => 3, + Abc::D => 4, + Abc::B(_) => 2, + Abc::A(_) => 1, + } +} + +fn foo2(x: Abc) -> bool { + match x { + Abc::D => true, + _ => false + } +} + +fn main() { + assert_eq!(1, foo(Abc::A(42))); + assert_eq!(2, foo(Abc::B(-100))); + assert_eq!(3, foo(Abc::C)); + assert_eq!(4, foo(Abc::D)); + + assert_eq!(false, foo2(Abc::A(1))); + assert_eq!(false, foo2(Abc::B(2))); + assert_eq!(false, foo2(Abc::C)); + assert_eq!(true, foo2(Abc::D)); +} diff --git a/src/test/run-pass/mir_codegen_switchint.rs b/src/test/run-pass/mir_codegen_switchint.rs new file mode 100644 index 00000000000..537734596a5 --- /dev/null +++ b/src/test/run-pass/mir_codegen_switchint.rs @@ -0,0 +1,21 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +pub fn foo(x: i8) -> i32 { + match x { + 1 => 0, + _ => 1, + } +} + +fn main() { + assert_eq!(foo(0), 1); + assert_eq!(foo(1), 0); +} diff --git a/src/test/run-pass/mir_overflow_off.rs b/src/test/run-pass/mir_overflow_off.rs index 0db1e7b4563..a2cfca01dac 100644 --- a/src/test/run-pass/mir_overflow_off.rs +++ b/src/test/run-pass/mir_overflow_off.rs @@ -10,7 +10,7 @@ // compile-flags: -Z force-overflow-checks=off -// Test that with MIR trans, overflow checks can be +// Test that with MIR codegen, overflow checks can be // turned off, even when they're from core::ops::*. use std::ops::*; diff --git a/src/test/run-pass/mir_trans_array.rs b/src/test/run-pass/mir_trans_array.rs deleted file mode 100644 index b7f247012ce..00000000000 --- a/src/test/run-pass/mir_trans_array.rs +++ /dev/null @@ -1,19 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn into_inner() -> [u64; 1024] { - let mut x = 10 + 20; - [x; 1024] -} - -fn main(){ - let x: &[u64] = &[30; 1024]; - assert_eq!(&into_inner()[..], x); -} diff --git a/src/test/run-pass/mir_trans_array_2.rs b/src/test/run-pass/mir_trans_array_2.rs deleted file mode 100644 index c7133fb0c0e..00000000000 --- a/src/test/run-pass/mir_trans_array_2.rs +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn into_inner(x: u64) -> [u64; 1024] { - [x; 2*4*8*16] -} - -fn main(){ - let x: &[u64] = &[42; 1024]; - assert_eq!(&into_inner(42)[..], x); -} diff --git a/src/test/run-pass/mir_trans_call_converging.rs b/src/test/run-pass/mir_trans_call_converging.rs deleted file mode 100644 index 7d420bb86c6..00000000000 --- a/src/test/run-pass/mir_trans_call_converging.rs +++ /dev/null @@ -1,26 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn converging_fn() -> u64 { - 43 -} - -fn mir() -> u64 { - let x; - loop { - x = converging_fn(); - break; - } - x -} - -fn main() { - assert_eq!(mir(), 43); -} diff --git a/src/test/run-pass/mir_trans_calls.rs b/src/test/run-pass/mir_trans_calls.rs deleted file mode 100644 index d02e3287bc3..00000000000 --- a/src/test/run-pass/mir_trans_calls.rs +++ /dev/null @@ -1,200 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![feature(fn_traits, test)] - -extern crate test; - -fn test1(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { - // Test passing a number of arguments including a fat pointer. - // Also returning via an out pointer - fn callee(a: isize, b: (i32, i32), c: &[i32]) -> (isize, (i32, i32), &[i32]) { - (a, b, c) - } - callee(a, b, c) -} - -fn test2(a: isize) -> isize { - // Test passing a single argument. - // Not using out pointer. - fn callee(a: isize) -> isize { - a - } - callee(a) -} - -#[derive(PartialEq, Eq, Debug)] -struct Foo; -impl Foo { - fn inherent_method(&self, a: isize) -> isize { a } -} - -fn test3(x: &Foo, a: isize) -> isize { - // Test calling inherent method - x.inherent_method(a) -} - -trait Bar { - fn extension_method(&self, a: isize) -> isize { a } -} -impl Bar for Foo {} - -fn test4(x: &Foo, a: isize) -> isize { - // Test calling extension method - x.extension_method(a) -} - -fn test5(x: &Bar, a: isize) -> isize { - // Test calling method on trait object - x.extension_method(a) -} - -fn test6(x: &T, a: isize) -> isize { - // Test calling extension method on generic callee - x.extension_method(a) -} - -trait One { - fn one() -> T; -} -impl One for isize { - fn one() -> isize { 1 } -} - -fn test7() -> isize { - // Test calling trait static method - ::one() -} - -struct Two; -impl Two { - fn two() -> isize { 2 } -} - -fn test8() -> isize { - // Test calling impl static method - Two::two() -} - -extern fn simple_extern(x: u32, y: (u32, u32)) -> u32 { - x + y.0 * y.1 -} - -fn test9() -> u32 { - simple_extern(41, (42, 43)) -} - -fn test_closure(f: &F, x: i32, y: i32) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f(x, y) -} - -fn test_fn_object(f: &Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { - f(x, y) -} - -fn test_fn_impl(f: &&Fn(i32, i32) -> i32, x: i32, y: i32) -> i32 { - // This call goes through the Fn implementation for &Fn provided in - // core::ops::impls. It expands to a static Fn::call() that calls the - // Fn::call() implementation of the object shim underneath. - f(x, y) -} - -fn test_fn_direct_call(f: &F, x: i32, y: i32) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f.call((x, y)) -} - -fn test_fn_const_call(f: &F) -> i32 - where F: Fn(i32, i32) -> i32 -{ - f.call((100, -1)) -} - -fn test_fn_nil_call(f: &F) -> i32 - where F: Fn() -> i32 -{ - f() -} - -fn test_fn_transmute_zst(x: ()) -> [(); 1] { - fn id(x: T) -> T {x} - - id(unsafe { - std::mem::transmute(x) - }) -} - -fn test_fn_ignored_pair() -> ((), ()) { - ((), ()) -} - -fn test_fn_ignored_pair_0() { - test_fn_ignored_pair().0 -} - -fn id(x: T) -> T { x } - -fn ignored_pair_named() -> (Foo, Foo) { - (Foo, Foo) -} - -fn test_fn_ignored_pair_named() -> (Foo, Foo) { - id(ignored_pair_named()) -} - -fn test_fn_nested_pair(x: &((f32, f32), u32)) -> (f32, f32) { - let y = *x; - let z = y.0; - (z.0, z.1) -} - -fn test_fn_const_arg_by_ref(mut a: [u64; 4]) -> u64 { - // Mutate the by-reference argument, which won't work with - // a non-immediate constant unless it's copied to the stack. - let a = test::black_box(&mut a); - a[0] += a[1]; - a[0] += a[2]; - a[0] += a[3]; - a[0] -} - -fn main() { - assert_eq!(test1(1, (2, 3), &[4, 5, 6]), (1, (2, 3), &[4, 5, 6][..])); - assert_eq!(test2(98), 98); - assert_eq!(test3(&Foo, 42), 42); - assert_eq!(test4(&Foo, 970), 970); - assert_eq!(test5(&Foo, 8576), 8576); - assert_eq!(test6(&Foo, 12367), 12367); - assert_eq!(test7(), 1); - assert_eq!(test8(), 2); - assert_eq!(test9(), 41 + 42 * 43); - - let r = 3; - let closure = |x: i32, y: i32| { r*(x + (y*2)) }; - assert_eq!(test_fn_const_call(&closure), 294); - assert_eq!(test_closure(&closure, 100, 1), 306); - let function_object = &closure as &Fn(i32, i32) -> i32; - assert_eq!(test_fn_object(function_object, 100, 2), 312); - assert_eq!(test_fn_impl(&function_object, 100, 3), 318); - assert_eq!(test_fn_direct_call(&closure, 100, 4), 324); - - assert_eq!(test_fn_nil_call(&(|| 42)), 42); - assert_eq!(test_fn_transmute_zst(()), [()]); - - assert_eq!(test_fn_ignored_pair_0(), ()); - assert_eq!(test_fn_ignored_pair_named(), (Foo, Foo)); - assert_eq!(test_fn_nested_pair(&((1.0, 2.0), 0)), (1.0, 2.0)); - - const ARRAY: [u64; 4] = [1, 2, 3, 4]; - assert_eq!(test_fn_const_arg_by_ref(ARRAY), 1 + 2 + 3 + 4); -} diff --git a/src/test/run-pass/mir_trans_calls_variadic.rs b/src/test/run-pass/mir_trans_calls_variadic.rs deleted file mode 100644 index 7845c9426e2..00000000000 --- a/src/test/run-pass/mir_trans_calls_variadic.rs +++ /dev/null @@ -1,31 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// ignore-wasm32-bare no libc to test ffi with - -#[link(name = "rust_test_helpers", kind = "static")] -extern { - fn rust_interesting_average(_: i64, ...) -> f64; -} - -fn test(a: i64, b: i64, c: i64, d: i64, e: i64, f: T, g: U) -> i64 { - unsafe { - rust_interesting_average(6, a, a as f64, - b, b as f64, - c, c as f64, - d, d as f64, - e, e as f64, - f, g) as i64 - } -} - -fn main(){ - assert_eq!(test(10, 20, 30, 40, 50, 60_i64, 60.0_f64), 70); -} diff --git a/src/test/run-pass/mir_trans_critical_edge.rs b/src/test/run-pass/mir_trans_critical_edge.rs deleted file mode 100644 index f6fe19c4309..00000000000 --- a/src/test/run-pass/mir_trans_critical_edge.rs +++ /dev/null @@ -1,52 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This code produces a CFG with critical edges that, if we don't -// handle properly, will cause invalid codegen. - -#![feature(rustc_attrs)] - -enum State { - Both, - Front, - Back -} - -pub struct Foo { - state: State, - a: A, - b: B -} - -impl Foo -where A: Iterator, B: Iterator -{ - // This is the function we care about - fn next(&mut self) -> Option { - match self.state { - State::Both => match self.a.next() { - elt @ Some(..) => elt, - None => { - self.state = State::Back; - self.b.next() - } - }, - State::Front => self.a.next(), - State::Back => self.b.next(), - } - } -} - -// Make sure we actually translate a version of the function -pub fn do_stuff(mut f: Foo>, Box>>) { - let _x = f.next(); -} - -fn main() {} diff --git a/src/test/run-pass/mir_trans_spike1.rs b/src/test/run-pass/mir_trans_spike1.rs deleted file mode 100644 index 8474e841e01..00000000000 --- a/src/test/run-pass/mir_trans_spike1.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// A simple spike test for MIR version of trans. - -fn sum(x: i32, y: i32) -> i32 { - x + y -} - -fn main() { - let x = sum(22, 44); - assert_eq!(x, 66); - println!("sum()={:?}", x); -} diff --git a/src/test/run-pass/mir_trans_switch.rs b/src/test/run-pass/mir_trans_switch.rs deleted file mode 100644 index b097bf46ad3..00000000000 --- a/src/test/run-pass/mir_trans_switch.rs +++ /dev/null @@ -1,44 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -enum Abc { - A(u8), - B(i8), - C, - D, -} - -fn foo(x: Abc) -> i32 { - match x { - Abc::C => 3, - Abc::D => 4, - Abc::B(_) => 2, - Abc::A(_) => 1, - } -} - -fn foo2(x: Abc) -> bool { - match x { - Abc::D => true, - _ => false - } -} - -fn main() { - assert_eq!(1, foo(Abc::A(42))); - assert_eq!(2, foo(Abc::B(-100))); - assert_eq!(3, foo(Abc::C)); - assert_eq!(4, foo(Abc::D)); - - assert_eq!(false, foo2(Abc::A(1))); - assert_eq!(false, foo2(Abc::B(2))); - assert_eq!(false, foo2(Abc::C)); - assert_eq!(true, foo2(Abc::D)); -} diff --git a/src/test/run-pass/mir_trans_switchint.rs b/src/test/run-pass/mir_trans_switchint.rs deleted file mode 100644 index 537734596a5..00000000000 --- a/src/test/run-pass/mir_trans_switchint.rs +++ /dev/null @@ -1,21 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -pub fn foo(x: i8) -> i32 { - match x { - 1 => 0, - _ => 1, - } -} - -fn main() { - assert_eq!(foo(0), 1); - assert_eq!(foo(1), 0); -} diff --git a/src/test/run-pass/pattern-bound-var-in-for-each.rs b/src/test/run-pass/pattern-bound-var-in-for-each.rs index 59ead3e3e98..778f355b24b 100644 --- a/src/test/run-pass/pattern-bound-var-in-for-each.rs +++ b/src/test/run-pass/pattern-bound-var-in-for-each.rs @@ -8,8 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -// Tests that trans_path checks whether a -// pattern-bound var is an upvar (when translating +// Tests that codegen_path checks whether a +// pattern-bound var is an upvar (when codegenning // the for-each body) diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs new file mode 100644 index 00000000000..60a7f70931d --- /dev/null +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -0,0 +1,62 @@ +// Copyright 2012 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// pretty-expanded FIXME #23616 + +#![feature(allocator_api)] + +use std::alloc::{Alloc, Global, Layout, oom}; +use std::ptr::NonNull; + +struct arena(()); + +struct Bcx<'a> { + fcx: &'a Fcx<'a> +} + +struct Fcx<'a> { + arena: &'a arena, + ccx: &'a Ccx +} + +struct Ccx { + x: isize +} + +fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { + unsafe { + let ptr = Global.alloc(Layout::new::()) + .unwrap_or_else(|_| oom()); + &*(ptr.as_ptr() as *const _) + } +} + +fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { + return alloc(bcx.fcx.arena); +} + +fn g(fcx : &Fcx) { + let bcx = Bcx { fcx: fcx }; + let bcx2 = h(&bcx); + unsafe { + Global.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); + } +} + +fn f(ccx : &Ccx) { + let a = arena(()); + let fcx = Fcx { arena: &a, ccx: ccx }; + return g(&fcx); +} + +pub fn main() { + let ccx = Ccx { x: 0 }; + f(&ccx); +} diff --git a/src/test/run-pass/regions-mock-trans.rs b/src/test/run-pass/regions-mock-trans.rs deleted file mode 100644 index 60a7f70931d..00000000000 --- a/src/test/run-pass/regions-mock-trans.rs +++ /dev/null @@ -1,62 +0,0 @@ -// Copyright 2012 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// pretty-expanded FIXME #23616 - -#![feature(allocator_api)] - -use std::alloc::{Alloc, Global, Layout, oom}; -use std::ptr::NonNull; - -struct arena(()); - -struct Bcx<'a> { - fcx: &'a Fcx<'a> -} - -struct Fcx<'a> { - arena: &'a arena, - ccx: &'a Ccx -} - -struct Ccx { - x: isize -} - -fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { - unsafe { - let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| oom()); - &*(ptr.as_ptr() as *const _) - } -} - -fn h<'a>(bcx : &'a Bcx<'a>) -> &'a Bcx<'a> { - return alloc(bcx.fcx.arena); -} - -fn g(fcx : &Fcx) { - let bcx = Bcx { fcx: fcx }; - let bcx2 = h(&bcx); - unsafe { - Global.dealloc(NonNull::new_unchecked(bcx2 as *const _ as *mut _), Layout::new::()); - } -} - -fn f(ccx : &Ccx) { - let a = arena(()); - let fcx = Fcx { arena: &a, ccx: ccx }; - return g(&fcx); -} - -pub fn main() { - let ccx = Ccx { x: 0 }; - f(&ccx); -} diff --git a/src/test/run-pass/sepcomp-fns-backwards.rs b/src/test/run-pass/sepcomp-fns-backwards.rs index 1ab8bc7f88c..4fea07028b6 100644 --- a/src/test/run-pass/sepcomp-fns-backwards.rs +++ b/src/test/run-pass/sepcomp-fns-backwards.rs @@ -11,7 +11,7 @@ // ignore-bitrig // compile-flags: -C codegen-units=3 -// Test references to items that haven't been translated yet. +// Test references to items that haven't been codegened yet. // Generate some code in the first compilation unit before declaring any // modules. This ensures that the first module doesn't go into the same diff --git a/src/test/run-pass/small-enum-range-edge.rs b/src/test/run-pass/small-enum-range-edge.rs index 56abdf6e20a..d2283da8bdd 100644 --- a/src/test/run-pass/small-enum-range-edge.rs +++ b/src/test/run-pass/small-enum-range-edge.rs @@ -13,7 +13,7 @@ #![feature(core)] /*! - * Tests the range assertion wraparound case in trans::middle::adt::load_discr. + * Tests the range assertion wraparound case when reading discriminants. */ #[repr(u8)] diff --git a/src/test/run-pass/trans-object-shim.rs b/src/test/run-pass/trans-object-shim.rs deleted file mode 100644 index 5fbfef05e10..00000000000 --- a/src/test/run-pass/trans-object-shim.rs +++ /dev/null @@ -1,14 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -fn main() { - assert_eq!((ToString::to_string as fn(&(ToString+'static)) -> String)(&"foo"), - String::from("foo")); -} diff --git a/src/test/run-pass/trans-tag-static-padding.rs b/src/test/run-pass/trans-tag-static-padding.rs deleted file mode 100644 index ba01d51dc6a..00000000000 --- a/src/test/run-pass/trans-tag-static-padding.rs +++ /dev/null @@ -1,67 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - - -// Issue #13186 - -// For simplicity of explanations assuming code is compiled for x86_64 -// Linux ABI. - -// Size of TestOption is 16, and alignment of TestOption is 8. -// Size of u8 is 1, and alignment of u8 is 1. -// So size of Request is 24, and alignment of Request must be 8: -// the maximum alignment of its fields. -// Last 7 bytes of Request struct are not occupied by any fields. - - - -enum TestOption { - TestNone, - TestSome(T), -} - -pub struct Request { - foo: TestOption, - bar: u8, -} - -fn default_instance() -> &'static Request { - static instance: Request = Request { - // LLVM does not allow to specify alignment of expressions, thus - // alignment of `foo` in constant is 1, not 8. - foo: TestOption::TestNone, - bar: 17, - // Space after last field is not occupied by any data, but it is - // reserved to make struct aligned properly. If compiler does - // not insert padding after last field when emitting constant, - // size of struct may be not equal to size of struct, and - // compiler crashes in internal assertion check. - }; - &instance -} - -fn non_default_instance() -> &'static Request { - static instance: Request = Request { - foo: TestOption::TestSome(0x1020304050607080), - bar: 19, - }; - &instance -} - -pub fn main() { - match default_instance() { - &Request { foo: TestOption::TestNone, bar: 17 } => {}, - _ => panic!(), - }; - match non_default_instance() { - &Request { foo: TestOption::TestSome(0x1020304050607080), bar: 19 } => {}, - _ => panic!(), - }; -} diff --git a/src/test/run-pass/union/union-const-codegen.rs b/src/test/run-pass/union/union-const-codegen.rs new file mode 100644 index 00000000000..77270364bb5 --- /dev/null +++ b/src/test/run-pass/union/union-const-codegen.rs @@ -0,0 +1,25 @@ +// Copyright 2016 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +union U { + a: u64, + b: u64, +} + +const C: U = U { b: 10 }; + +fn main() { + unsafe { + let a = C.a; + let b = C.b; + assert_eq!(a, 10); + assert_eq!(b, 10); + } +} diff --git a/src/test/run-pass/union/union-const-trans.rs b/src/test/run-pass/union/union-const-trans.rs deleted file mode 100644 index 77270364bb5..00000000000 --- a/src/test/run-pass/union/union-const-trans.rs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright 2016 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -union U { - a: u64, - b: u64, -} - -const C: U = U { b: 10 }; - -fn main() { - unsafe { - let a = C.a; - let b = C.b; - assert_eq!(a, 10); - assert_eq!(b, 10); - } -} diff --git a/src/test/run-pass/zero-sized-tuple-struct.rs b/src/test/run-pass/zero-sized-tuple-struct.rs index aaffdc4ec7c..9625d6a88ac 100644 --- a/src/test/run-pass/zero-sized-tuple-struct.rs +++ b/src/test/run-pass/zero-sized-tuple-struct.rs @@ -10,7 +10,7 @@ #![allow(unused_assignments)] -// Make sure that the constructor args are translated for zero-sized tuple structs +// Make sure that the constructor args are codegened for zero-sized tuple structs struct Foo(()); diff --git a/src/tools/compiletest/src/header.rs b/src/tools/compiletest/src/header.rs index 06ec9e893f0..f66f5c5b70e 100644 --- a/src/tools/compiletest/src/header.rs +++ b/src/tools/compiletest/src/header.rs @@ -224,7 +224,7 @@ pub struct TestProps { // The test must be compiled and run successfully. Only used in UI tests for now. pub run_pass: bool, // Skip any codegen step and running the executable. Only for run-pass. - pub skip_trans: bool, + pub skip_codegen: bool, // Do not pass `-Z ui-testing` to UI tests pub disable_ui_testing_normalization: bool, // customized normalization rules @@ -258,7 +258,7 @@ impl TestProps { compile_pass: false, check_test_line_numbers_match: false, run_pass: false, - skip_trans: false, + skip_codegen: false, disable_ui_testing_normalization: false, normalize_stdout: vec![], normalize_stderr: vec![], @@ -371,8 +371,8 @@ impl TestProps { self.compile_pass = config.parse_compile_pass(ln) || self.run_pass; } - if !self.skip_trans { - self.skip_trans = config.parse_skip_trans(ln); + if !self.skip_codegen { + self.skip_codegen = config.parse_skip_codegen(ln); } if !self.disable_ui_testing_normalization { @@ -532,8 +532,8 @@ impl Config { self.parse_name_directive(line, "run-pass") } - fn parse_skip_trans(&self, line: &str) -> bool { - self.parse_name_directive(line, "skip-trans") + fn parse_skip_codegen(&self, line: &str) -> bool { + self.parse_name_directive(line, "skip-codegen") } fn parse_env(&self, line: &str, name: &str) -> Option<(String, String)> { diff --git a/src/tools/compiletest/src/runtest.rs b/src/tools/compiletest/src/runtest.rs index 59d94e1fa51..780c8122734 100644 --- a/src/tools/compiletest/src/runtest.rs +++ b/src/tools/compiletest/src/runtest.rs @@ -357,7 +357,7 @@ impl<'test> TestCx<'test> { "run-pass tests with expected warnings should be moved to ui/" ); - if !self.props.skip_trans { + if !self.props.skip_codegen { let proc_res = self.exec_compiled_test(); if !proc_res.status.success() { self.fatal_proc_rec("test run failed!", &proc_res); @@ -555,7 +555,7 @@ impl<'test> TestCx<'test> { rustc .arg("-") - .arg("-Zno-trans") + .arg("-Zno-codegen") .arg("--out-dir") .arg(&out_dir) .arg(&format!("--target={}", target)) @@ -1703,7 +1703,7 @@ impl<'test> TestCx<'test> { } } - if self.props.skip_trans { + if self.props.skip_codegen { assert!(!self.props.compile_flags.iter().any(|s| s.starts_with("--emit"))); rustc.args(&["--emit", "metadata"]); } @@ -2181,19 +2181,19 @@ impl<'test> TestCx<'test> { self.check_no_compiler_crash(&proc_res); - const PREFIX: &'static str = "TRANS_ITEM "; + const PREFIX: &'static str = "MONO_ITEM "; const CGU_MARKER: &'static str = "@@"; - let actual: Vec = proc_res + let actual: Vec = proc_res .stdout .lines() .filter(|line| line.starts_with(PREFIX)) - .map(str_to_trans_item) + .map(str_to_mono_item) .collect(); - let expected: Vec = errors::load_errors(&self.testpaths.file, None) + let expected: Vec = errors::load_errors(&self.testpaths.file, None) .iter() - .map(|e| str_to_trans_item(&e.msg[..])) + .map(|e| str_to_mono_item(&e.msg[..])) .collect(); let mut missing = Vec::new(); @@ -2271,14 +2271,14 @@ impl<'test> TestCx<'test> { } #[derive(Clone, Eq, PartialEq)] - struct TransItem { + struct MonoItem { name: String, codegen_units: HashSet, string: String, } - // [TRANS_ITEM] name [@@ (cgu)+] - fn str_to_trans_item(s: &str) -> TransItem { + // [MONO_ITEM] name [@@ (cgu)+] + fn str_to_mono_item(s: &str) -> MonoItem { let s = if s.starts_with(PREFIX) { (&s[PREFIX.len()..]).trim() } else { @@ -2307,7 +2307,7 @@ impl<'test> TestCx<'test> { HashSet::new() }; - TransItem { + MonoItem { name: name.to_owned(), codegen_units: cgus, string: full_string, diff --git a/src/tools/tidy/src/deps.rs b/src/tools/tidy/src/deps.rs index c34cf1bd5ec..5739ec5f325 100644 --- a/src/tools/tidy/src/deps.rs +++ b/src/tools/tidy/src/deps.rs @@ -52,7 +52,7 @@ static EXCEPTIONS: &'static [&'static str] = &[ /// Which crates to check against the whitelist? static WHITELIST_CRATES: &'static [CrateVersion] = &[ CrateVersion("rustc", "0.0.0"), - CrateVersion("rustc_trans", "0.0.0"), + CrateVersion("rustc_codegen_llvm", "0.0.0"), ]; /// Whitelist of crates rustc is allowed to depend on. Avoid adding to the list if possible. -- cgit 1.4.1-3-g733a5 From 9e3432447a9c6386443acdf731d488c159be3f66 Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Thu, 10 May 2018 12:02:19 -0600 Subject: Switch to 1.26 bootstrap compiler --- src/bootstrap/channel.rs | 2 +- src/liballoc/Cargo.toml | 2 + src/liballoc/alloc.rs | 47 +- src/liballoc/lib.rs | 10 - src/liballoc/slice.rs | 13 +- src/liballoc/str.rs | 7 +- src/liballoc/vec.rs | 3 - src/liballoc_jemalloc/lib.rs | 7 - src/liballoc_system/lib.rs | 27 - src/libarena/lib.rs | 1 - src/libcore/Cargo.toml | 2 + src/libcore/clone.rs | 1 - src/libcore/internal_macros.rs | 14 - src/libcore/lib.rs | 19 +- src/libcore/marker.rs | 1 - src/libcore/num/f32.rs | 4 +- src/libcore/num/f64.rs | 4 +- src/libcore/num/mod.rs | 2 - src/libcore/ops/range.rs | 10 - src/libcore/prelude/v1.rs | 10 - src/libcore/slice/mod.rs | 987 ++++++++----------------------- src/libcore/str/mod.rs | 590 ++++-------------- src/libcore/tests/lib.rs | 1 - src/libcore/tests/num/uint_macros.rs | 1 - src/librustc/lib.rs | 1 - src/librustc_mir/borrow_check/nll/mod.rs | 4 +- src/librustc_mir/lib.rs | 13 +- src/librustc_mir/util/pretty.rs | 8 +- src/librustc_typeck/lib.rs | 2 - src/librustdoc/lib.rs | 2 - src/libstd/alloc.rs | 61 -- src/libstd/f32.rs | 12 +- src/libstd/f64.rs | 12 +- src/libstd/lib.rs | 9 +- src/libsyntax_ext/asm.rs | 21 +- src/libsyntax_ext/lib.rs | 3 +- src/rtstartup/rsbegin.rs | 31 +- src/stage0.txt | 2 +- 38 files changed, 434 insertions(+), 1512 deletions(-) (limited to 'src/liballoc') diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index 3453933a965..a2495f68c1f 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -24,7 +24,7 @@ use Build; use config::Config; // The version number -pub const CFG_RELEASE_NUM: &str = "1.27.0"; +pub const CFG_RELEASE_NUM: &str = "1.28.0"; pub struct GitInfo { inner: Option, diff --git a/src/liballoc/Cargo.toml b/src/liballoc/Cargo.toml index 6383bd1e941..ada21e04b30 100644 --- a/src/liballoc/Cargo.toml +++ b/src/liballoc/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "alloc" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "alloc" diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index f59c9f7fd61..b4b82e6ecff 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -22,28 +22,6 @@ use core::usize; #[doc(inline)] pub use core::alloc::*; -#[cfg(stage0)] -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_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; -} - -#[cfg(not(stage0))] extern "Rust" { #[allocator] #[rustc_allocator_nounwind] @@ -74,10 +52,7 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } @@ -88,20 +63,13 @@ unsafe impl GlobalAlloc for Global { #[inline] unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); - #[cfg(stage0)] - let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), - new_size, layout.align(), &mut 0); ptr as *mut Opaque } #[inline] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - #[cfg(not(stage0))] let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); - #[cfg(stage0)] - let ptr = __rust_alloc_zeroed(layout.size(), layout.align(), &mut 0); ptr as *mut Opaque } } @@ -152,14 +120,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { } } -#[cfg(stage0)] -#[lang = "box_free"] -#[inline] -unsafe fn old_box_free(ptr: *mut T) { - box_free(Unique::new_unchecked(ptr)) -} - -#[cfg_attr(not(any(test, stage0)), lang = "box_free")] +#[cfg_attr(not(test), lang = "box_free")] #[inline] pub(crate) unsafe fn box_free(ptr: Unique) { let ptr = ptr.as_ptr(); @@ -172,12 +133,6 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } -#[cfg(stage0)] -pub fn oom() -> ! { - unsafe { ::core::intrinsics::abort() } -} - -#[cfg(not(stage0))] pub fn oom() -> ! { extern { #[lang = "oom"] diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index f7dd9d4f010..91de3ad0c39 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -75,7 +75,6 @@ #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand -#![cfg_attr(all(not(test), stage0), feature(float_internals))] #![cfg_attr(not(test), feature(exact_size_is_empty))] #![cfg_attr(not(test), feature(generator_trait))] #![cfg_attr(test, feature(rand, test))] @@ -90,13 +89,10 @@ #![feature(collections_range)] #![feature(const_fn)] #![feature(core_intrinsics)] -#![cfg_attr(stage0, feature(core_slice_ext))] -#![cfg_attr(stage0, feature(core_str_ext))] #![feature(custom_attribute)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] #![feature(fmt_internals)] -#![cfg_attr(stage0, feature(fn_must_use))] #![feature(from_ref)] #![feature(fundamental)] #![feature(lang_items)] @@ -122,7 +118,6 @@ #![feature(exact_chunks)] #![feature(pointer_methods)] #![feature(inclusive_range_methods)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(rustc_const_unstable)] #![feature(const_vec_new)] @@ -157,15 +152,10 @@ pub mod alloc; #[unstable(feature = "allocator_api", issue = "32838")] #[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] /// Use the `alloc` module instead. -#[cfg(not(stage0))] pub mod heap { pub use alloc::*; } -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -#[cfg(stage0)] -pub mod heap; // Primitive types using the heaps above diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 6caf12aa7eb..4427ac004f9 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -101,7 +101,6 @@ use core::cmp::Ordering::{self, Less}; use core::mem::size_of; use core::mem; use core::ptr; -#[cfg(stage0)] use core::slice::SliceExt; use core::{u8, u16, u32}; use borrow::{Borrow, BorrowMut, ToOwned}; @@ -171,13 +170,9 @@ mod hack { } } -#[cfg_attr(stage0, lang = "slice")] -#[cfg_attr(not(stage0), lang = "slice_alloc")] +#[lang = "slice_alloc"] #[cfg(not(test))] impl [T] { - #[cfg(stage0)] - slice_core_methods!(); - /// Sorts the slice. /// /// This sort is stable (i.e. does not reorder equal elements) and `O(n log n)` worst-case. @@ -467,8 +462,7 @@ impl [T] { } } -#[cfg_attr(stage0, lang = "slice_u8")] -#[cfg_attr(not(stage0), lang = "slice_u8_alloc")] +#[lang = "slice_u8_alloc"] #[cfg(not(test))] impl [u8] { /// Returns a vector containing a copy of this slice where each byte @@ -504,9 +498,6 @@ impl [u8] { me.make_ascii_lowercase(); me } - - #[cfg(stage0)] - slice_u8_core_methods!(); } //////////////////////////////////////////////////////////////////////////////// diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 42efdea74b1..c10c0a69433 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -40,7 +40,6 @@ use core::fmt; use core::str as core_str; -#[cfg(stage0)] use core::str::StrExt; use core::str::pattern::Pattern; use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; @@ -158,13 +157,9 @@ impl ToOwned for str { } /// Methods for string slices. -#[cfg_attr(stage0, lang = "str")] -#[cfg_attr(not(stage0), lang = "str_alloc")] +#[lang = "str_alloc"] #[cfg(not(test))] impl str { - #[cfg(stage0)] - str_core_methods!(); - /// Converts a `Box` into a `Box<[u8]>` without copying or allocating. /// /// # Examples diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index d30f8cd0fca..bf89b377b7e 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -73,9 +73,6 @@ use core::intrinsics::{arith_offset, assume}; use core::iter::{FromIterator, FusedIterator, TrustedLen}; use core::marker::PhantomData; use core::mem; -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; use core::ops::Bound::{Excluded, Included, Unbounded}; use core::ops::{Index, IndexMut, RangeBounds}; use core::ops; diff --git a/src/liballoc_jemalloc/lib.rs b/src/liballoc_jemalloc/lib.rs index 4b8755877de..ce856eccd83 100644 --- a/src/liballoc_jemalloc/lib.rs +++ b/src/liballoc_jemalloc/lib.rs @@ -97,13 +97,6 @@ mod contents { ptr } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rde_oom() -> ! { - ::core::intrinsics::abort(); - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rde_dealloc(ptr: *mut u8, diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 7376ac0f15d..9490b54e675 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -73,33 +73,6 @@ unsafe impl Alloc for System { } } -#[cfg(stage0)] -#[unstable(feature = "allocator_api", issue = "32838")] -unsafe impl<'a> Alloc for &'a System { - #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(*self, layout)).ok_or(AllocErr) - } - - #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - GlobalAlloc::dealloc(*self, ptr.as_ptr(), layout) - } - - #[inline] - unsafe fn realloc(&mut self, - ptr: NonNull, - layout: Layout, - new_size: usize) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::realloc(*self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) - } -} - #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { use core::alloc::{GlobalAlloc, Opaque, Layout}; diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index c79e0e14e3d..f7143a4f981 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -26,7 +26,6 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![cfg_attr(test, feature(test))] #![allow(deprecated)] diff --git a/src/libcore/Cargo.toml b/src/libcore/Cargo.toml index 24529f7a9d8..321ed892ea9 100644 --- a/src/libcore/Cargo.toml +++ b/src/libcore/Cargo.toml @@ -2,6 +2,8 @@ authors = ["The Rust Project Developers"] name = "core" version = "0.0.0" +autotests = false +autobenches = false [lib] name = "core" diff --git a/src/libcore/clone.rs b/src/libcore/clone.rs index f79f7351698..3b15ba2b4ab 100644 --- a/src/libcore/clone.rs +++ b/src/libcore/clone.rs @@ -153,7 +153,6 @@ pub struct AssertParamIsCopy { _field: ::marker::PhantomData { - $(#[$attr])* pub $($Item)* - } -} - -#[cfg(not(stage0))] -macro_rules! public_in_stage0 { - ( { $(#[$attr:meta])* } $($Item: tt)*) => { - $(#[$attr])* pub(crate) $($Item)* - } -} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 06fbfcecba8..77b5488084d 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -112,18 +112,13 @@ #![feature(unwind_attributes)] #![feature(doc_alias)] #![feature(inclusive_range_methods)] - -#![cfg_attr(not(stage0), feature(mmx_target_feature))] -#![cfg_attr(not(stage0), feature(tbm_target_feature))] -#![cfg_attr(not(stage0), feature(sse4a_target_feature))] -#![cfg_attr(not(stage0), feature(arm_target_feature))] -#![cfg_attr(not(stage0), feature(powerpc_target_feature))] -#![cfg_attr(not(stage0), feature(mips_target_feature))] -#![cfg_attr(not(stage0), feature(aarch64_target_feature))] - -#![cfg_attr(stage0, feature(target_feature))] -#![cfg_attr(stage0, feature(cfg_target_feature))] -#![cfg_attr(stage0, feature(fn_must_use))] +#![feature(mmx_target_feature)] +#![feature(tbm_target_feature)] +#![feature(sse4a_target_feature)] +#![feature(arm_target_feature)] +#![feature(powerpc_target_feature)] +#![feature(mips_target_feature)] +#![feature(aarch64_target_feature)] #[prelude_import] #[allow(unused)] diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index db5f50a99ca..6c8ee0eda11 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -611,7 +611,6 @@ pub unsafe auto trait Unpin {} /// /// Implementations that cannot be described in Rust /// are implemented in `SelectionContext::copy_clone_conditions()` in librustc. -#[cfg(not(stage0))] mod copy_impls { use super::Copy; diff --git a/src/libcore/num/f32.rs b/src/libcore/num/f32.rs index 4a7dc13f0f2..718dd42a615 100644 --- a/src/libcore/num/f32.rs +++ b/src/libcore/num/f32.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f32`. @@ -277,7 +277,6 @@ impl Float for f32 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f32_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -553,7 +552,6 @@ macro_rules! f32_core_methods { () => { #[lang = "f32"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f32 { f32_core_methods!(); } diff --git a/src/libcore/num/f64.rs b/src/libcore/num/f64.rs index 801de5e87bd..f128c55c78a 100644 --- a/src/libcore/num/f64.rs +++ b/src/libcore/num/f64.rs @@ -19,7 +19,7 @@ use mem; use num::Float; -#[cfg(not(stage0))] use num::FpCategory; +use num::FpCategory; use num::FpCategory as Fp; /// The radix or base of the internal representation of `f64`. @@ -276,7 +276,6 @@ impl Float for f64 { // FIXME: remove (inline) this macro and the Float trait // when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] #[unstable(feature = "core_float", issue = "32110")] macro_rules! f64_core_methods { () => { /// Returns `true` if this value is `NaN` and false otherwise. @@ -562,7 +561,6 @@ macro_rules! f64_core_methods { () => { #[lang = "f64"] #[cfg(not(test))] -#[cfg(not(stage0))] impl f64 { f64_core_methods!(); } diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 6df8ca98ba9..58d45b107f1 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -422,7 +422,6 @@ $EndFeature, " /// assert_eq!(m, -22016); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { (self as $UnsignedT).reverse_bits() as Self @@ -2194,7 +2193,6 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " /// assert_eq!(m, 43520); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { unsafe { intrinsics::bitreverse(self as $ActualT) as Self } diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 697e6a3efde..1f84631ada3 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -335,18 +335,8 @@ pub struct RangeInclusive { // but it is known that LLVM is not able to optimize loops following that RFC. // Consider adding an extra `bool` field to indicate emptiness of the range. // See #45222 for performance test cases. - #[cfg(not(stage0))] pub(crate) start: Idx, - #[cfg(not(stage0))] pub(crate) end: Idx, - /// The lower bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub start: Idx, - /// The upper bound of the range (inclusive). - #[cfg(stage0)] - #[unstable(feature = "inclusive_range_fields", issue = "49022")] - pub end: Idx, } impl RangeInclusive { diff --git a/src/libcore/prelude/v1.rs b/src/libcore/prelude/v1.rs index 8212648f2d8..45f629a6442 100644 --- a/src/libcore/prelude/v1.rs +++ b/src/libcore/prelude/v1.rs @@ -54,13 +54,3 @@ pub use option::Option::{self, Some, None}; #[stable(feature = "core_prelude", since = "1.4.0")] #[doc(no_inline)] pub use result::Result::{self, Ok, Err}; - -// Re-exported extension traits for primitive types -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use slice::SliceExt; -#[stable(feature = "core_prelude", since = "1.4.0")] -#[doc(no_inline)] -#[cfg(stage0)] -pub use str::StrExt; diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 93ebc23ac0b..82891b691dc 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -68,181 +68,6 @@ struct Repr { // Extension traits // -public_in_stage0! { -{ -/// Extension methods for slices. -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -#[allow(missing_docs)] // documented elsewhere -} -trait SliceExt { - type Item; - - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&[Self::Item], &[Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn iter(&self) -> Iter; - - #[stable(feature = "core", since = "1.6.0")] - fn split

(&self, pred: P) -> Split - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit

(&self, pred: P) -> RSplit - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn

(&self, n: usize, pred: P) -> SplitN - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn

(&self, n: usize, pred: P) -> RSplitN - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn windows(&self, size: usize) -> Windows; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks(&self, size: usize) -> Chunks; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks(&self, size: usize) -> ExactChunks; - - #[stable(feature = "core", since = "1.6.0")] - fn get(&self, index: I) -> Option<&I::Output> - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn first(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last(&self) -> Option<(&Self::Item, &[Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last(&self) -> Option<&Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked(&self, index: I) -> &I::Output - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn as_ptr(&self) -> *const Self::Item; - - #[stable(feature = "core", since = "1.6.0")] - fn binary_search(&self, x: &Self::Item) -> Result - where Self::Item: Ord; - - #[stable(feature = "core", since = "1.6.0")] - fn binary_search_by<'a, F>(&'a self, f: F) -> Result - where F: FnMut(&'a Self::Item) -> Ordering; - - #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result - where F: FnMut(&'a Self::Item) -> B, - B: Ord; - - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[stable(feature = "core", since = "1.6.0")] - fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn iter_mut(&mut self) -> IterMut; - - #[stable(feature = "core", since = "1.6.0")] - fn first_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_first_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_last_mut(&mut self) -> Option<(&mut Self::Item, &mut [Self::Item])>; - - #[stable(feature = "core", since = "1.6.0")] - fn last_mut(&mut self) -> Option<&mut Self::Item>; - - #[stable(feature = "core", since = "1.6.0")] - fn split_mut

(&mut self, pred: P) -> SplitMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "slice_rsplit", since = "1.27.0")] - fn rsplit_mut

(&mut self, pred: P) -> RSplitMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn splitn_mut

(&mut self, n: usize, pred: P) -> SplitNMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn_mut

(&mut self, n: usize, pred: P) -> RSplitNMut - where P: FnMut(&Self::Item) -> bool; - - #[stable(feature = "core", since = "1.6.0")] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut; - - #[unstable(feature = "exact_chunks", issue = "47115")] - fn exact_chunks_mut(&mut self, size: usize) -> ExactChunksMut; - - #[stable(feature = "core", since = "1.6.0")] - fn swap(&mut self, a: usize, b: usize); - - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut [Self::Item], &mut [Self::Item]); - - #[stable(feature = "core", since = "1.6.0")] - fn reverse(&mut self); - - #[stable(feature = "core", since = "1.6.0")] - unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where I: SliceIndex; - #[stable(feature = "core", since = "1.6.0")] - fn as_mut_ptr(&mut self) -> *mut Self::Item; - - #[stable(feature = "core", since = "1.6.0")] - fn contains(&self, x: &Self::Item) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn starts_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "core", since = "1.6.0")] - fn ends_with(&self, needle: &[Self::Item]) -> bool where Self::Item: PartialEq; - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_left(&mut self, mid: usize); - - #[stable(feature = "slice_rotate", since = "1.26.0")] - fn rotate_right(&mut self, k: usize); - - #[stable(feature = "clone_from_slice", since = "1.7.0")] - fn clone_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Clone; - - #[stable(feature = "copy_from_slice", since = "1.9.0")] - fn copy_from_slice(&mut self, src: &[Self::Item]) where Self::Item: Copy; - - #[stable(feature = "swap_with_slice", since = "1.27.0")] - fn swap_with_slice(&mut self, src: &mut [Self::Item]); - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable(&mut self) - where Self::Item: Ord; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by(&mut self, compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering; - - #[stable(feature = "sort_unstable", since = "1.20.0")] - fn sort_unstable_by_key(&mut self, f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord; -}} - // Use macros to be generic over const/mut macro_rules! slice_offset { ($ptr:expr, $by:expr) => {{ @@ -281,488 +106,9 @@ macro_rules! make_ref_mut { }}; } -#[unstable(feature = "core_slice_ext", - reason = "stable interface provided by `impl [T]` in later crates", - issue = "32110")] -impl SliceExt for [T] { - type Item = T; - - #[inline] - fn split_at(&self, mid: usize) -> (&[T], &[T]) { - (&self[..mid], &self[mid..]) - } - - #[inline] - fn iter(&self) -> Iter { - unsafe { - let p = if mem::size_of::() == 0 { - 1 as *const _ - } else { - let p = self.as_ptr(); - assume(!p.is_null()); - p - }; - - Iter { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn split

(&self, pred: P) -> Split - where P: FnMut(&T) -> bool - { - Split { - v: self, - pred, - finished: false - } - } - - #[inline] - fn rsplit

(&self, pred: P) -> RSplit - where P: FnMut(&T) -> bool - { - RSplit { inner: self.split(pred) } - } - - #[inline] - fn splitn

(&self, n: usize, pred: P) -> SplitN - where P: FnMut(&T) -> bool - { - SplitN { - inner: GenericSplitN { - iter: self.split(pred), - count: n - } - } - } - - #[inline] - fn rsplitn

(&self, n: usize, pred: P) -> RSplitN - where P: FnMut(&T) -> bool - { - RSplitN { - inner: GenericSplitN { - iter: self.rsplit(pred), - count: n - } - } - } - - #[inline] - fn windows(&self, size: usize) -> Windows { - assert!(size != 0); - Windows { v: self, size: size } - } - - #[inline] - fn chunks(&self, chunk_size: usize) -> Chunks { - assert!(chunk_size != 0); - Chunks { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunks { v: &self[..len], chunk_size: chunk_size} - } - - #[inline] - fn get(&self, index: I) -> Option<&I::Output> - where I: SliceIndex<[T]> - { - index.get(self) - } - - #[inline] - fn first(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[0]) } - } - - #[inline] - fn split_first(&self) -> Option<(&T, &[T])> { - if self.is_empty() { None } else { Some((&self[0], &self[1..])) } - } - - #[inline] - fn split_last(&self) -> Option<(&T, &[T])> { - let len = self.len(); - if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } - } - - #[inline] - fn last(&self) -> Option<&T> { - if self.is_empty() { None } else { Some(&self[self.len() - 1]) } - } - - #[inline] - unsafe fn get_unchecked(&self, index: I) -> &I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked(self) - } - - #[inline] - fn as_ptr(&self) -> *const T { - self as *const [T] as *const T - } - - fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result - where F: FnMut(&'a T) -> Ordering - { - let s = self; - let mut size = s.len(); - if size == 0 { - return Err(0); - } - let mut base = 0usize; - while size > 1 { - let half = size / 2; - let mid = base + half; - // mid is always in [0, size), that means mid is >= 0 and < size. - // mid >= 0: by definition - // mid < size: mid = size / 2 + size / 4 + size / 8 ... - let cmp = f(unsafe { s.get_unchecked(mid) }); - base = if cmp == Greater { base } else { mid }; - size -= half; - } - // base is always in [0, size) because base <= mid. - let cmp = f(unsafe { s.get_unchecked(base) }); - if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } - } - - #[inline] - fn len(&self) -> usize { - unsafe { - mem::transmute::<&[T], Repr>(self).len - } - } - - #[inline] - fn get_mut(&mut self, index: I) -> Option<&mut I::Output> - where I: SliceIndex<[T]> - { - index.get_mut(self) - } - - #[inline] - fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - let len = self.len(); - let ptr = self.as_mut_ptr(); - - unsafe { - assert!(mid <= len); - - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } - - #[inline] - fn iter_mut(&mut self) -> IterMut { - unsafe { - let p = if mem::size_of::() == 0 { - 1 as *mut _ - } else { - let p = self.as_mut_ptr(); - assume(!p.is_null()); - p - }; - - IterMut { - ptr: p, - end: slice_offset!(p, self.len() as isize), - _marker: marker::PhantomData - } - } - } - - #[inline] - fn last_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if len == 0 { return None; } - Some(&mut self[len - 1]) - } - - #[inline] - fn first_mut(&mut self) -> Option<&mut T> { - if self.is_empty() { None } else { Some(&mut self[0]) } - } - - #[inline] - fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - if self.is_empty() { None } else { - let split = self.split_at_mut(1); - Some((&mut split.0[0], split.1)) - } - } - - #[inline] - fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - let len = self.len(); - if len == 0 { None } else { - let split = self.split_at_mut(len - 1); - Some((&mut split.1[0], split.0)) - } - } - - #[inline] - fn split_mut

(&mut self, pred: P) -> SplitMut - where P: FnMut(&T) -> bool - { - SplitMut { v: self, pred: pred, finished: false } - } - - #[inline] - fn rsplit_mut

(&mut self, pred: P) -> RSplitMut - where P: FnMut(&T) -> bool - { - RSplitMut { inner: self.split_mut(pred) } - } - - #[inline] - fn splitn_mut

(&mut self, n: usize, pred: P) -> SplitNMut - where P: FnMut(&T) -> bool - { - SplitNMut { - inner: GenericSplitN { - iter: self.split_mut(pred), - count: n - } - } - } - - #[inline] - fn rsplitn_mut

(&mut self, n: usize, pred: P) -> RSplitNMut where - P: FnMut(&T) -> bool, - { - RSplitNMut { - inner: GenericSplitN { - iter: self.rsplit_mut(pred), - count: n - } - } - } - - #[inline] - fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { - assert!(chunk_size != 0); - ChunksMut { v: self, chunk_size: chunk_size } - } - - #[inline] - fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { - assert!(chunk_size != 0); - let rem = self.len() % chunk_size; - let len = self.len() - rem; - ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} - } - - #[inline] - fn swap(&mut self, a: usize, b: usize) { - unsafe { - // Can't take two mutable loans from one vector, so instead just cast - // them to their raw pointers to do the swap - let pa: *mut T = &mut self[a]; - let pb: *mut T = &mut self[b]; - ptr::swap(pa, pb); - } - } - - fn reverse(&mut self) { - let mut i: usize = 0; - let ln = self.len(); - - // For very small types, all the individual reads in the normal - // path perform poorly. We can do better, given efficient unaligned - // load/store, by loading a larger chunk and reversing a register. - - // Ideally LLVM would do this for us, as it knows better than we do - // whether unaligned reads are efficient (since that changes between - // different ARM versions, for example) and what the best chunk size - // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls - // the loop, so we need to do this ourselves. (Hypothesis: reverse - // is troublesome because the sides can be aligned differently -- - // will be, when the length is odd -- so there's no way of emitting - // pre- and postludes to use fully-aligned SIMD in the middle.) - - let fast_unaligned = - cfg!(any(target_arch = "x86", target_arch = "x86_64")); - - if fast_unaligned && mem::size_of::() == 1 { - // Use the llvm.bswap intrinsic to reverse u8s in a usize - let chunk = mem::size_of::(); - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut usize); - let vb = ptr::read_unaligned(pb as *mut usize); - ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); - ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); - } - i += chunk; - } - } - - if fast_unaligned && mem::size_of::() == 2 { - // Use rotate-by-16 to reverse u16s in a u32 - let chunk = mem::size_of::() / 2; - while i + chunk - 1 < ln / 2 { - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); - let va = ptr::read_unaligned(pa as *mut u32); - let vb = ptr::read_unaligned(pb as *mut u32); - ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); - ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); - } - i += chunk; - } - } - - while i < ln / 2 { - // Unsafe swap to avoid the bounds check in safe swap. - unsafe { - let pa: *mut T = self.get_unchecked_mut(i); - let pb: *mut T = self.get_unchecked_mut(ln - i - 1); - ptr::swap(pa, pb); - } - i += 1; - } - } - - #[inline] - unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output - where I: SliceIndex<[T]> - { - index.get_unchecked_mut(self) - } - - #[inline] - fn as_mut_ptr(&mut self) -> *mut T { - self as *mut [T] as *mut T - } - - #[inline] - fn contains(&self, x: &T) -> bool where T: PartialEq { - x.slice_contains(self) - } - - #[inline] - fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - let n = needle.len(); - self.len() >= n && needle == &self[..n] - } - - #[inline] - fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - let (m, n) = (self.len(), needle.len()); - m >= n && needle == &self[m-n..] - } - - fn binary_search(&self, x: &T) -> Result - where T: Ord - { - self.binary_search_by(|p| p.cmp(x)) - } - - fn rotate_left(&mut self, mid: usize) { - assert!(mid <= self.len()); - let k = self.len() - mid; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - fn rotate_right(&mut self, k: usize) { - assert!(k <= self.len()); - let mid = self.len() - k; - - unsafe { - let p = self.as_mut_ptr(); - rotate::ptr_rotate(mid, p.offset(mid as isize), k); - } - } - - #[inline] - fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - // NOTE: We need to explicitly slice them to the same length - // for bounds checking to be elided, and the optimizer will - // generate memcpy for simple cases (for example T = u8). - let len = self.len(); - let src = &src[..len]; - for i in 0..len { - self[i].clone_from(&src[i]); - } - } - - #[inline] - fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::copy_nonoverlapping( - src.as_ptr(), self.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn swap_with_slice(&mut self, src: &mut [T]) { - assert!(self.len() == src.len(), - "destination and source slices have different lengths"); - unsafe { - ptr::swap_nonoverlapping( - self.as_mut_ptr(), src.as_mut_ptr(), self.len()); - } - } - - #[inline] - fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result - where F: FnMut(&'a Self::Item) -> B, - B: Ord - { - self.binary_search_by(|k| f(k).cmp(b)) - } - - #[inline] - fn sort_unstable(&mut self) - where Self::Item: Ord - { - sort::quicksort(self, |a, b| a.lt(b)); - } - - #[inline] - fn sort_unstable_by(&mut self, mut compare: F) - where F: FnMut(&Self::Item, &Self::Item) -> Ordering - { - sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); - } - - #[inline] - fn sort_unstable_by_key(&mut self, mut f: F) - where F: FnMut(&Self::Item) -> B, - B: Ord - { - sort::quicksort(self, |a, b| f(a).lt(&f(b))); - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_core_methods { () => { +#[lang = "slice"] +#[cfg(not(test))] +impl [T] { /// Returns the number of elements in the slice. /// /// # Examples @@ -774,7 +120,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - SliceExt::len(self) + unsafe { + mem::transmute::<&[T], Repr>(self).len + } } /// Returns `true` if the slice has a length of 0. @@ -788,7 +136,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn is_empty(&self) -> bool { - SliceExt::is_empty(self) + self.len() == 0 } /// Returns the first element of the slice, or `None` if it is empty. @@ -805,7 +153,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first(&self) -> Option<&T> { - SliceExt::first(self) + if self.is_empty() { None } else { Some(&self[0]) } } /// Returns a mutable pointer to the first element of the slice, or `None` if it is empty. @@ -823,7 +171,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn first_mut(&mut self) -> Option<&mut T> { - SliceExt::first_mut(self) + if self.is_empty() { None } else { Some(&mut self[0]) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -841,7 +189,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first(&self) -> Option<(&T, &[T])> { - SliceExt::split_first(self) + if self.is_empty() { None } else { Some((&self[0], &self[1..])) } } /// Returns the first and all the rest of the elements of the slice, or `None` if it is empty. @@ -861,7 +209,10 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_first_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_first_mut(self) + if self.is_empty() { None } else { + let split = self.split_at_mut(1); + Some((&mut split.0[0], split.1)) + } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -879,7 +230,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last(&self) -> Option<(&T, &[T])> { - SliceExt::split_last(self) + let len = self.len(); + if len == 0 { None } else { Some((&self[len - 1], &self[..(len - 1)])) } } /// Returns the last and all the rest of the elements of the slice, or `None` if it is empty. @@ -899,7 +251,12 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "slice_splits", since = "1.5.0")] #[inline] pub fn split_last_mut(&mut self) -> Option<(&mut T, &mut [T])> { - SliceExt::split_last_mut(self) + let len = self.len(); + if len == 0 { None } else { + let split = self.split_at_mut(len - 1); + Some((&mut split.1[0], split.0)) + } + } /// Returns the last element of the slice, or `None` if it is empty. @@ -916,7 +273,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last(&self) -> Option<&T> { - SliceExt::last(self) + if self.is_empty() { None } else { Some(&self[self.len() - 1]) } } /// Returns a mutable pointer to the last item in the slice. @@ -934,7 +291,9 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn last_mut(&mut self) -> Option<&mut T> { - SliceExt::last_mut(self) + let len = self.len(); + if len == 0 { return None; } + Some(&mut self[len - 1]) } /// Returns a reference to an element or subslice depending on the type of @@ -959,7 +318,7 @@ macro_rules! slice_core_methods { () => { pub fn get(&self, index: I) -> Option<&I::Output> where I: SliceIndex { - SliceExt::get(self, index) + index.get(self) } /// Returns a mutable reference to an element or subslice depending on the @@ -982,7 +341,7 @@ macro_rules! slice_core_methods { () => { pub fn get_mut(&mut self, index: I) -> Option<&mut I::Output> where I: SliceIndex { - SliceExt::get_mut(self, index) + index.get_mut(self) } /// Returns a reference to an element or subslice, without doing bounds @@ -1007,7 +366,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked(&self, index: I) -> &I::Output where I: SliceIndex { - SliceExt::get_unchecked(self, index) + index.get_unchecked(self) } /// Returns a mutable reference to an element or subslice, without doing @@ -1034,7 +393,7 @@ macro_rules! slice_core_methods { () => { pub unsafe fn get_unchecked_mut(&mut self, index: I) -> &mut I::Output where I: SliceIndex { - SliceExt::get_unchecked_mut(self, index) + index.get_unchecked_mut(self) } /// Returns a raw pointer to the slice's buffer. @@ -1060,7 +419,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const T { - SliceExt::as_ptr(self) + self as *const [T] as *const T } /// Returns an unsafe mutable pointer to the slice's buffer. @@ -1087,7 +446,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_mut_ptr(&mut self) -> *mut T { - SliceExt::as_mut_ptr(self) + self as *mut [T] as *mut T } /// Swaps two elements in the slice. @@ -1111,7 +470,13 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn swap(&mut self, a: usize, b: usize) { - SliceExt::swap(self, a, b) + unsafe { + // Can't take two mutable loans from one vector, so instead just cast + // them to their raw pointers to do the swap + let pa: *mut T = &mut self[a]; + let pb: *mut T = &mut self[b]; + ptr::swap(pa, pb); + } } /// Reverses the order of elements in the slice, in place. @@ -1126,7 +491,66 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn reverse(&mut self) { - SliceExt::reverse(self) + let mut i: usize = 0; + let ln = self.len(); + + // For very small types, all the individual reads in the normal + // path perform poorly. We can do better, given efficient unaligned + // load/store, by loading a larger chunk and reversing a register. + + // Ideally LLVM would do this for us, as it knows better than we do + // whether unaligned reads are efficient (since that changes between + // different ARM versions, for example) and what the best chunk size + // would be. Unfortunately, as of LLVM 4.0 (2017-05) it only unrolls + // the loop, so we need to do this ourselves. (Hypothesis: reverse + // is troublesome because the sides can be aligned differently -- + // will be, when the length is odd -- so there's no way of emitting + // pre- and postludes to use fully-aligned SIMD in the middle.) + + let fast_unaligned = + cfg!(any(target_arch = "x86", target_arch = "x86_64")); + + if fast_unaligned && mem::size_of::() == 1 { + // Use the llvm.bswap intrinsic to reverse u8s in a usize + let chunk = mem::size_of::(); + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut usize); + let vb = ptr::read_unaligned(pb as *mut usize); + ptr::write_unaligned(pa as *mut usize, vb.swap_bytes()); + ptr::write_unaligned(pb as *mut usize, va.swap_bytes()); + } + i += chunk; + } + } + + if fast_unaligned && mem::size_of::() == 2 { + // Use rotate-by-16 to reverse u16s in a u32 + let chunk = mem::size_of::() / 2; + while i + chunk - 1 < ln / 2 { + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - chunk); + let va = ptr::read_unaligned(pa as *mut u32); + let vb = ptr::read_unaligned(pb as *mut u32); + ptr::write_unaligned(pa as *mut u32, vb.rotate_left(16)); + ptr::write_unaligned(pb as *mut u32, va.rotate_left(16)); + } + i += chunk; + } + } + + while i < ln / 2 { + // Unsafe swap to avoid the bounds check in safe swap. + unsafe { + let pa: *mut T = self.get_unchecked_mut(i); + let pb: *mut T = self.get_unchecked_mut(ln - i - 1); + ptr::swap(pa, pb); + } + i += 1; + } } /// Returns an iterator over the slice. @@ -1145,7 +569,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter(&self) -> Iter { - SliceExt::iter(self) + unsafe { + let p = if mem::size_of::() == 0 { + 1 as *const _ + } else { + let p = self.as_ptr(); + assume(!p.is_null()); + p + }; + + Iter { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator that allows modifying each value. @@ -1162,7 +600,21 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn iter_mut(&mut self) -> IterMut { - SliceExt::iter_mut(self) + unsafe { + let p = if mem::size_of::() == 0 { + 1 as *mut _ + } else { + let p = self.as_mut_ptr(); + assume(!p.is_null()); + p + }; + + IterMut { + ptr: p, + end: slice_offset!(p, self.len() as isize), + _marker: marker::PhantomData + } + } } /// Returns an iterator over all contiguous windows of length @@ -1194,7 +646,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn windows(&self, size: usize) -> Windows { - SliceExt::windows(self, size) + assert!(size != 0); + Windows { v: self, size: size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1224,7 +677,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks(&self, chunk_size: usize) -> Chunks { - SliceExt::chunks(self, chunk_size) + assert!(chunk_size != 0); + Chunks { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a @@ -1256,7 +710,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks(&self, chunk_size: usize) -> ExactChunks { - SliceExt::exact_chunks(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunks { v: &self[..len], chunk_size: chunk_size} } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1290,7 +747,8 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chunks_mut(&mut self, chunk_size: usize) -> ChunksMut { - SliceExt::chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + ChunksMut { v: self, chunk_size: chunk_size } } /// Returns an iterator over `chunk_size` elements of the slice at a time. @@ -1328,7 +786,10 @@ macro_rules! slice_core_methods { () => { #[unstable(feature = "exact_chunks", issue = "47115")] #[inline] pub fn exact_chunks_mut(&mut self, chunk_size: usize) -> ExactChunksMut { - SliceExt::exact_chunks_mut(self, chunk_size) + assert!(chunk_size != 0); + let rem = self.len() % chunk_size; + let len = self.len() - rem; + ExactChunksMut { v: &mut self[..len], chunk_size: chunk_size} } /// Divides one slice into two at an index. @@ -1367,7 +828,7 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at(&self, mid: usize) -> (&[T], &[T]) { - SliceExt::split_at(self, mid) + (&self[..mid], &self[mid..]) } /// Divides one mutable slice into two at an index. @@ -1397,7 +858,15 @@ macro_rules! slice_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_at_mut(&mut self, mid: usize) -> (&mut [T], &mut [T]) { - SliceExt::split_at_mut(self, mid) + let len = self.len(); + let ptr = self.as_mut_ptr(); + + unsafe { + assert!(mid <= len); + + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } } /// Returns an iterator over subslices separated by elements that match @@ -1445,7 +914,11 @@ macro_rules! slice_core_methods { () => { pub fn split(&self, pred: F) -> Split where F: FnMut(&T) -> bool { - SliceExt::split(self, pred) + Split { + v: self, + pred, + finished: false + } } /// Returns an iterator over mutable subslices separated by elements that @@ -1466,7 +939,7 @@ macro_rules! slice_core_methods { () => { pub fn split_mut(&mut self, pred: F) -> SplitMut where F: FnMut(&T) -> bool { - SliceExt::split_mut(self, pred) + SplitMut { v: self, pred: pred, finished: false } } /// Returns an iterator over subslices separated by elements that match @@ -1501,7 +974,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit(&self, pred: F) -> RSplit where F: FnMut(&T) -> bool { - SliceExt::rsplit(self, pred) + RSplit { inner: self.split(pred) } } /// Returns an iterator over mutable subslices separated by elements that @@ -1526,7 +999,7 @@ macro_rules! slice_core_methods { () => { pub fn rsplit_mut(&mut self, pred: F) -> RSplitMut where F: FnMut(&T) -> bool { - SliceExt::rsplit_mut(self, pred) + RSplitMut { inner: self.split_mut(pred) } } /// Returns an iterator over subslices separated by elements that match @@ -1553,7 +1026,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn(&self, n: usize, pred: F) -> SplitN where F: FnMut(&T) -> bool { - SliceExt::splitn(self, n, pred) + SplitN { + inner: GenericSplitN { + iter: self.split(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1578,7 +1056,12 @@ macro_rules! slice_core_methods { () => { pub fn splitn_mut(&mut self, n: usize, pred: F) -> SplitNMut where F: FnMut(&T) -> bool { - SliceExt::splitn_mut(self, n, pred) + SplitNMut { + inner: GenericSplitN { + iter: self.split_mut(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1606,7 +1089,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn(&self, n: usize, pred: F) -> RSplitN where F: FnMut(&T) -> bool { - SliceExt::rsplitn(self, n, pred) + RSplitN { + inner: GenericSplitN { + iter: self.rsplit(pred), + count: n + } + } } /// Returns an iterator over subslices separated by elements that match @@ -1632,7 +1120,12 @@ macro_rules! slice_core_methods { () => { pub fn rsplitn_mut(&mut self, n: usize, pred: F) -> RSplitNMut where F: FnMut(&T) -> bool { - SliceExt::rsplitn_mut(self, n, pred) + RSplitNMut { + inner: GenericSplitN { + iter: self.rsplit_mut(pred), + count: n + } + } } /// Returns `true` if the slice contains an element with the given value. @@ -1648,7 +1141,7 @@ macro_rules! slice_core_methods { () => { pub fn contains(&self, x: &T) -> bool where T: PartialEq { - SliceExt::contains(self, x) + x.slice_contains(self) } /// Returns `true` if `needle` is a prefix of the slice. @@ -1675,7 +1168,8 @@ macro_rules! slice_core_methods { () => { pub fn starts_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::starts_with(self, needle) + let n = needle.len(); + self.len() >= n && needle == &self[..n] } /// Returns `true` if `needle` is a suffix of the slice. @@ -1702,7 +1196,8 @@ macro_rules! slice_core_methods { () => { pub fn ends_with(&self, needle: &[T]) -> bool where T: PartialEq { - SliceExt::ends_with(self, needle) + let (m, n) = (self.len(), needle.len()); + m >= n && needle == &self[m-n..] } /// Binary searches this sorted slice for a given element. @@ -1731,7 +1226,7 @@ macro_rules! slice_core_methods { () => { pub fn binary_search(&self, x: &T) -> Result where T: Ord { - SliceExt::binary_search(self, x) + self.binary_search_by(|p| p.cmp(x)) } /// Binary searches this sorted slice with a comparator function. @@ -1767,10 +1262,29 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] #[inline] - pub fn binary_search_by<'a, F>(&'a self, f: F) -> Result + pub fn binary_search_by<'a, F>(&'a self, mut f: F) -> Result where F: FnMut(&'a T) -> Ordering { - SliceExt::binary_search_by(self, f) + let s = self; + let mut size = s.len(); + if size == 0 { + return Err(0); + } + let mut base = 0usize; + while size > 1 { + let half = size / 2; + let mid = base + half; + // mid is always in [0, size), that means mid is >= 0 and < size. + // mid >= 0: by definition + // mid < size: mid = size / 2 + size / 4 + size / 8 ... + let cmp = f(unsafe { s.get_unchecked(mid) }); + base = if cmp == Greater { base } else { mid }; + size -= half; + } + // base is always in [0, size) because base <= mid. + let cmp = f(unsafe { s.get_unchecked(base) }); + if cmp == Equal { Ok(base) } else { Err(base + (cmp == Less) as usize) } + } /// Binary searches this sorted slice with a key extraction function. @@ -1805,11 +1319,11 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_binary_search_by_key", since = "1.10.0")] #[inline] - pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, f: F) -> Result + pub fn binary_search_by_key<'a, B, F>(&'a self, b: &B, mut f: F) -> Result where F: FnMut(&'a T) -> B, B: Ord { - SliceExt::binary_search_by_key(self, b, f) + self.binary_search_by(|k| f(k).cmp(b)) } /// Sorts the slice, but may not preserve the order of equal elements. @@ -1843,7 +1357,7 @@ macro_rules! slice_core_methods { () => { pub fn sort_unstable(&mut self) where T: Ord { - SliceExt::sort_unstable(self); + sort::quicksort(self, |a, b| a.lt(b)); } /// Sorts the slice with a comparator function, but may not preserve the order of equal @@ -1878,10 +1392,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by(&mut self, compare: F) + pub fn sort_unstable_by(&mut self, mut compare: F) where F: FnMut(&T, &T) -> Ordering { - SliceExt::sort_unstable_by(self, compare); + sort::quicksort(self, |a, b| compare(a, b) == Ordering::Less); } /// Sorts the slice with a key extraction function, but may not preserve the order of equal @@ -1910,10 +1424,10 @@ macro_rules! slice_core_methods { () => { /// [pdqsort]: https://github.com/orlp/pdqsort #[stable(feature = "sort_unstable", since = "1.20.0")] #[inline] - pub fn sort_unstable_by_key(&mut self, f: F) + pub fn sort_unstable_by_key(&mut self, mut f: F) where F: FnMut(&T) -> K, K: Ord { - SliceExt::sort_unstable_by_key(self, f); + sort::quicksort(self, |a, b| f(a).lt(&f(b))); } /// Rotates the slice in-place such that the first `mid` elements of the @@ -1948,7 +1462,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_left(&mut self, mid: usize) { - SliceExt::rotate_left(self, mid); + assert!(mid <= self.len()); + let k = self.len() - mid; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Rotates the slice in-place such that the first `self.len() - k` @@ -1983,7 +1503,13 @@ macro_rules! slice_core_methods { () => { /// ``` #[stable(feature = "slice_rotate", since = "1.26.0")] pub fn rotate_right(&mut self, k: usize) { - SliceExt::rotate_right(self, k); + assert!(k <= self.len()); + let mid = self.len() - k; + + unsafe { + let p = self.as_mut_ptr(); + rotate::ptr_rotate(mid, p.offset(mid as isize), k); + } } /// Copies the elements from `src` into `self`. @@ -2040,7 +1566,17 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "clone_from_slice", since = "1.7.0")] pub fn clone_from_slice(&mut self, src: &[T]) where T: Clone { - SliceExt::clone_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + // NOTE: We need to explicitly slice them to the same length + // for bounds checking to be elided, and the optimizer will + // generate memcpy for simple cases (for example T = u8). + let len = self.len(); + let src = &src[..len]; + for i in 0..len { + self[i].clone_from(&src[i]); + } + } /// Copies all elements from `src` into `self`, using a memcpy. @@ -2096,7 +1632,12 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "copy_from_slice", since = "1.9.0")] pub fn copy_from_slice(&mut self, src: &[T]) where T: Copy { - SliceExt::copy_from_slice(self, src) + assert!(self.len() == src.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::copy_nonoverlapping( + src.as_ptr(), self.as_mut_ptr(), self.len()); + } } /// Swaps all elements in `self` with those in `other`. @@ -2148,22 +1689,18 @@ macro_rules! slice_core_methods { () => { /// [`split_at_mut`]: #method.split_at_mut #[stable(feature = "swap_with_slice", since = "1.27.0")] pub fn swap_with_slice(&mut self, other: &mut [T]) { - SliceExt::swap_with_slice(self, other) + assert!(self.len() == other.len(), + "destination and source slices have different lengths"); + unsafe { + ptr::swap_nonoverlapping( + self.as_mut_ptr(), other.as_mut_ptr(), self.len()); + } } -}} - -#[lang = "slice"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl [T] { - slice_core_methods!(); } -// FIXME: remove (inline) this macro -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_slice_ext", issue = "32110")] -macro_rules! slice_u8_core_methods { () => { +#[lang = "slice_u8"] +#[cfg(not(test))] +impl [u8] { /// Checks if all bytes in this slice are within the ASCII range. #[stable(feature = "ascii_methods_on_intrinsics", since = "1.23.0")] #[inline] @@ -2217,13 +1754,7 @@ macro_rules! slice_u8_core_methods { () => { byte.make_ascii_lowercase(); } } -}} -#[lang = "slice_u8"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl [u8] { - slice_u8_core_methods!(); } #[stable(feature = "rust1", since = "1.0.0")] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index df7b2f25a86..82bead0ab46 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -2097,119 +2097,7 @@ mod traits { (..self.end+1).index_mut(slice) } } - -} - -public_in_stage0! { -{ -/// Methods for string slices -#[allow(missing_docs)] -#[doc(hidden)] -#[unstable(feature = "core_str_ext", - reason = "stable interface provided by `impl str` in later crates", - issue = "32110")] } -trait StrExt { - // NB there are no docs here are they're all located on the StrExt trait in - // liballoc, not here. - - #[stable(feature = "core", since = "1.6.0")] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn chars(&self) -> Chars; - #[stable(feature = "core", since = "1.6.0")] - fn bytes(&self) -> Bytes; - #[stable(feature = "core", since = "1.6.0")] - fn char_indices(&self) -> CharIndices; - #[stable(feature = "core", since = "1.6.0")] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P>; - #[stable(feature = "core", since = "1.6.0")] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn lines(&self) -> Lines; - #[stable(feature = "core", since = "1.6.0")] - #[rustc_deprecated(since = "1.6.0", reason = "use lines() instead now")] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get>(&self, i: I) -> Option<&I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - fn get_mut>(&mut self, i: I) -> Option<&mut I::Output>; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked>(&self, i: I) -> &I::Output; - #[stable(feature = "str_checked_slicing", since = "1.20.0")] - unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str; - #[stable(feature = "core", since = "1.6.0")] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str; - #[stable(feature = "core", since = "1.6.0")] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a>; - #[stable(feature = "core", since = "1.6.0")] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str; - #[stable(feature = "core", since = "1.6.0")] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a>; - #[stable(feature = "is_char_boundary", since = "1.9.0")] - fn is_char_boundary(&self, index: usize) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn as_bytes(&self) -> &[u8]; - #[stable(feature = "str_mut_extras", since = "1.20.0")] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8]; - #[stable(feature = "core", since = "1.6.0")] - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option; - #[stable(feature = "core", since = "1.6.0")] - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option - where P::Searcher: ReverseSearcher<'a>; - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option; - #[stable(feature = "core", since = "1.6.0")] - fn split_at(&self, mid: usize) -> (&str, &str); - #[stable(feature = "core", since = "1.6.0")] - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str); - #[stable(feature = "core", since = "1.6.0")] - fn as_ptr(&self) -> *const u8; - #[stable(feature = "core", since = "1.6.0")] - fn len(&self) -> usize; - #[stable(feature = "core", since = "1.6.0")] - fn is_empty(&self) -> bool; - #[stable(feature = "core", since = "1.6.0")] - fn parse(&self) -> Result; - #[stable(feature = "split_whitespace", since = "1.1.0")] - fn split_whitespace<'a>(&'a self) -> SplitWhitespace<'a>; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_left(&self) -> &str; - #[stable(feature = "rust1", since = "1.0.0")] - fn trim_right(&self) -> &str; -}} // truncate `&str` to length at most equal to `max` // return `true` if it were truncated, and the new str. @@ -2255,307 +2143,9 @@ fn slice_error_fail(s: &str, begin: usize, end: usize) -> ! { index, ch, char_range, s_trunc, ellipsis); } -#[stable(feature = "core", since = "1.6.0")] -impl StrExt for str { - #[inline] - fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_contained_in(self) - } - - #[inline] - fn chars(&self) -> Chars { - Chars{iter: self.as_bytes().iter()} - } - - #[inline] - fn bytes(&self) -> Bytes { - Bytes(self.as_bytes().iter().cloned()) - } - - #[inline] - fn char_indices(&self) -> CharIndices { - CharIndices { front_offset: 0, iter: self.chars() } - } - - #[inline] - fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - Split(SplitInternal { - start: 0, - end: self.len(), - matcher: pat.into_searcher(self), - allow_trailing_empty: true, - finished: false, - }) - } - - #[inline] - fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplit(self.split(pat).0) - } - - #[inline] - fn splitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> SplitN<'a, P> { - SplitN(SplitNInternal { - iter: self.split(pat).0, - count, - }) - } - - #[inline] - fn rsplitn<'a, P: Pattern<'a>>(&'a self, count: usize, pat: P) -> RSplitN<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitN(self.splitn(count, pat).0) - } - - #[inline] - fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - SplitTerminator(SplitInternal { - allow_trailing_empty: false, - ..self.split(pat).0 - }) - } - - #[inline] - fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RSplitTerminator(self.split_terminator(pat).0) - } - - #[inline] - fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - Matches(MatchesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatches(self.matches(pat).0) - } - - #[inline] - fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) - } - - #[inline] - fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> - where P::Searcher: ReverseSearcher<'a> - { - RMatchIndices(self.match_indices(pat).0) - } - #[inline] - fn lines(&self) -> Lines { - Lines(self.split_terminator('\n').map(LinesAnyMap)) - } - - #[inline] - #[allow(deprecated)] - fn lines_any(&self) -> LinesAny { - LinesAny(self.lines()) - } - - #[inline] - fn get>(&self, i: I) -> Option<&I::Output> { - i.get(self) - } - - #[inline] - fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - i.get_mut(self) - } - - #[inline] - unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - i.get_unchecked(self) - } - - #[inline] - unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - i.get_unchecked_mut(self) - } - - #[inline] - unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - (begin..end).get_unchecked(self) - } - - #[inline] - unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - (begin..end).get_unchecked_mut(self) - } - - #[inline] - fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - pat.is_prefix_of(self) - } - - #[inline] - fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool - where P::Searcher: ReverseSearcher<'a> - { - pat.is_suffix_of(self) - } - - #[inline] - fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: DoubleEndedSearcher<'a> - { - let mut i = 0; - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((a, b)) = matcher.next_reject() { - i = a; - j = b; // Remember earliest known match, correct it below if - // last match is different - } - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, j) - } - } - - #[inline] - fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - let mut i = self.len(); - let mut matcher = pat.into_searcher(self); - if let Some((a, _)) = matcher.next_reject() { - i = a; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(i, self.len()) - } - } - - #[inline] - fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str - where P::Searcher: ReverseSearcher<'a> - { - let mut j = 0; - let mut matcher = pat.into_searcher(self); - if let Some((_, b)) = matcher.next_reject_back() { - j = b; - } - unsafe { - // Searcher is known to return valid indices - self.slice_unchecked(0, j) - } - } - - #[inline] - fn is_char_boundary(&self, index: usize) -> bool { - // 0 and len are always ok. - // Test for 0 explicitly so that it can optimize out the check - // easily and skip reading string data for that case. - if index == 0 || index == self.len() { return true; } - match self.as_bytes().get(index) { - None => false, - // This is bit magic equivalent to: b < 128 || b >= 192 - Some(&b) => (b as i8) >= -0x40, - } - } - - #[inline] - fn as_bytes(&self) -> &[u8] { - unsafe { &*(self as *const str as *const [u8]) } - } - - #[inline] - unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - &mut *(self as *mut str as *mut [u8]) - } - - fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - pat.into_searcher(self).next_match().map(|(i, _)| i) - } - - fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option - where P::Searcher: ReverseSearcher<'a> - { - pat.into_searcher(self).next_match_back().map(|(i, _)| i) - } - - fn find_str<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - self.find(pat) - } - - #[inline] - fn split_at(&self, mid: usize) -> (&str, &str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - unsafe { - (self.slice_unchecked(0, mid), - self.slice_unchecked(mid, self.len())) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - // is_char_boundary checks that the index is in [0, .len()] - if self.is_char_boundary(mid) { - let len = self.len(); - let ptr = self.as_ptr() as *mut u8; - unsafe { - (from_raw_parts_mut(ptr, mid), - from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) - } - } else { - slice_error_fail(self, 0, mid) - } - } - - #[inline] - fn as_ptr(&self) -> *const u8 { - self as *const str as *const u8 - } - - #[inline] - fn len(&self) -> usize { - self.as_bytes().len() - } - - #[inline] - fn is_empty(&self) -> bool { self.len() == 0 } - - #[inline] - fn parse(&self) -> Result { FromStr::from_str(self) } - - #[inline] - fn split_whitespace(&self) -> SplitWhitespace { - SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } - } - - #[inline] - fn trim(&self) -> &str { - self.trim_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_left(&self) -> &str { - self.trim_left_matches(|c: char| c.is_whitespace()) - } - - #[inline] - fn trim_right(&self) -> &str { - self.trim_right_matches(|c: char| c.is_whitespace()) - } -} - -// FIXME: remove (inline) this macro and the SliceExt trait -// when updating to a bootstrap compiler that has the new lang items. -#[cfg_attr(stage0, macro_export)] -#[unstable(feature = "core_str_ext", issue = "32110")] -macro_rules! str_core_methods { () => { +#[lang = "str"] +#[cfg(not(test))] +impl str { /// Returns the length of `self`. /// /// This length is in bytes, not [`char`]s or graphemes. In other words, @@ -2577,7 +2167,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn len(&self) -> usize { - StrExt::len(self) + self.as_bytes().len() } /// Returns `true` if `self` has a length of zero bytes. @@ -2596,7 +2186,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn is_empty(&self) -> bool { - StrExt::is_empty(self) + self.len() == 0 } /// Checks that `index`-th byte lies at the start and/or end of a @@ -2626,7 +2216,15 @@ macro_rules! str_core_methods { () => { #[stable(feature = "is_char_boundary", since = "1.9.0")] #[inline] pub fn is_char_boundary(&self, index: usize) -> bool { - StrExt::is_char_boundary(self, index) + // 0 and len are always ok. + // Test for 0 explicitly so that it can optimize out the check + // easily and skip reading string data for that case. + if index == 0 || index == self.len() { return true; } + match self.as_bytes().get(index) { + None => false, + // This is bit magic equivalent to: b < 128 || b >= 192 + Some(&b) => (b as i8) >= -0x40, + } } /// Converts a string slice to a byte slice. To convert the byte slice back @@ -2645,7 +2243,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline(always)] pub fn as_bytes(&self) -> &[u8] { - StrExt::as_bytes(self) + unsafe { &*(self as *const str as *const [u8]) } } /// Converts a mutable string slice to a mutable byte slice. To convert the @@ -2684,7 +2282,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_mut_extras", since = "1.20.0")] #[inline(always)] pub unsafe fn as_bytes_mut(&mut self) -> &mut [u8] { - StrExt::as_bytes_mut(self) + &mut *(self as *mut str as *mut [u8]) } /// Converts a string slice to a raw pointer. @@ -2706,7 +2304,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn as_ptr(&self) -> *const u8 { - StrExt::as_ptr(self) + self as *const str as *const u8 } /// Returns a subslice of `str`. @@ -2733,7 +2331,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get>(&self, i: I) -> Option<&I::Output> { - StrExt::get(self, i) + i.get(self) } /// Returns a mutable subslice of `str`. @@ -2767,7 +2365,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub fn get_mut>(&mut self, i: I) -> Option<&mut I::Output> { - StrExt::get_mut(self, i) + i.get_mut(self) } /// Returns a unchecked subslice of `str`. @@ -2799,7 +2397,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked>(&self, i: I) -> &I::Output { - StrExt::get_unchecked(self, i) + i.get_unchecked(self) } /// Returns a mutable, unchecked subslice of `str`. @@ -2831,7 +2429,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_checked_slicing", since = "1.20.0")] #[inline] pub unsafe fn get_unchecked_mut>(&mut self, i: I) -> &mut I::Output { - StrExt::get_unchecked_mut(self, i) + i.get_unchecked_mut(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2880,7 +2478,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { - StrExt::slice_unchecked(self, begin, end) + (begin..end).get_unchecked(self) } /// Creates a string slice from another string slice, bypassing safety @@ -2910,7 +2508,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_slice_mut", since = "1.5.0")] #[inline] pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { - StrExt::slice_mut_unchecked(self, begin, end) + (begin..end).get_unchecked_mut(self) } /// Divide one string slice into two at an index. @@ -2946,7 +2544,15 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at(&self, mid: usize) -> (&str, &str) { - StrExt::split_at(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + unsafe { + (self.slice_unchecked(0, mid), + self.slice_unchecked(mid, self.len())) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Divide one mutable string slice into two at an index. @@ -2983,7 +2589,17 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "str_split_at", since = "1.4.0")] pub fn split_at_mut(&mut self, mid: usize) -> (&mut str, &mut str) { - StrExt::split_at_mut(self, mid) + // is_char_boundary checks that the index is in [0, .len()] + if self.is_char_boundary(mid) { + let len = self.len(); + let ptr = self.as_ptr() as *mut u8; + unsafe { + (from_raw_parts_mut(ptr, mid), + from_raw_parts_mut(ptr.offset(mid as isize), len - mid)) + } + } else { + slice_error_fail(self, 0, mid) + } } /// Returns an iterator over the [`char`]s of a string slice. @@ -3035,8 +2651,9 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn chars(&self) -> Chars { - StrExt::chars(self) + Chars{iter: self.as_bytes().iter()} } + /// Returns an iterator over the [`char`]s of a string slice, and their /// positions. /// @@ -3091,7 +2708,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn char_indices(&self) -> CharIndices { - StrExt::char_indices(self) + CharIndices { front_offset: 0, iter: self.chars() } } /// An iterator over the bytes of a string slice. @@ -3116,7 +2733,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn bytes(&self) -> Bytes { - StrExt::bytes(self) + Bytes(self.as_bytes().iter().cloned()) } /// Split a string slice by whitespace. @@ -3156,7 +2773,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "split_whitespace", since = "1.1.0")] #[inline] pub fn split_whitespace(&self) -> SplitWhitespace { - StrExt::split_whitespace(self) + SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } /// An iterator over the lines of a string, as string slices. @@ -3198,7 +2815,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn lines(&self) -> Lines { - StrExt::lines(self) + Lines(self.split_terminator('\n').map(LinesAnyMap)) } /// An iterator over the lines of a string. @@ -3207,7 +2824,7 @@ macro_rules! str_core_methods { () => { #[inline] #[allow(deprecated)] pub fn lines_any(&self) -> LinesAny { - StrExt::lines_any(self) + LinesAny(self.lines()) } /// Returns an iterator of `u16` over the string encoded as UTF-16. @@ -3226,7 +2843,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "encode_utf16", since = "1.8.0")] pub fn encode_utf16(&self) -> EncodeUtf16 { - EncodeUtf16::new(self) + EncodeUtf16 { chars: self.chars(), extra: 0 } } /// Returns `true` if the given pattern matches a sub-slice of @@ -3247,7 +2864,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn contains<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::contains(self, pat) + pat.is_contained_in(self) } /// Returns `true` if the given pattern matches a prefix of this @@ -3267,7 +2884,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn starts_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool { - StrExt::starts_with(self, pat) + pat.is_prefix_of(self) } /// Returns `true` if the given pattern matches a suffix of this @@ -3289,7 +2906,7 @@ macro_rules! str_core_methods { () => { pub fn ends_with<'a, P: Pattern<'a>>(&'a self, pat: P) -> bool where P::Searcher: ReverseSearcher<'a> { - StrExt::ends_with(self, pat) + pat.is_suffix_of(self) } /// Returns the byte index of the first character of this string slice that @@ -3337,7 +2954,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn find<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option { - StrExt::find(self, pat) + pat.into_searcher(self).next_match().map(|(i, _)| i) } /// Returns the byte index of the last character of this string slice that @@ -3384,7 +3001,7 @@ macro_rules! str_core_methods { () => { pub fn rfind<'a, P: Pattern<'a>>(&'a self, pat: P) -> Option where P::Searcher: ReverseSearcher<'a> { - StrExt::rfind(self, pat) + pat.into_searcher(self).next_match_back().map(|(i, _)| i) } /// An iterator over substrings of this string slice, separated by @@ -3496,7 +3113,13 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split<'a, P: Pattern<'a>>(&'a self, pat: P) -> Split<'a, P> { - StrExt::split(self, pat) + Split(SplitInternal { + start: 0, + end: self.len(), + matcher: pat.into_searcher(self), + allow_trailing_empty: true, + finished: false, + }) } /// An iterator over substrings of the given string slice, separated by @@ -3548,7 +3171,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplit<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit(self, pat) + RSplit(self.split(pat).0) } /// An iterator over substrings of the given string slice, separated by @@ -3593,7 +3216,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn split_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> SplitTerminator<'a, P> { - StrExt::split_terminator(self, pat) + SplitTerminator(SplitInternal { + allow_trailing_empty: false, + ..self.split(pat).0 + }) } /// An iterator over substrings of `self`, separated by characters @@ -3639,7 +3265,7 @@ macro_rules! str_core_methods { () => { pub fn rsplit_terminator<'a, P: Pattern<'a>>(&'a self, pat: P) -> RSplitTerminator<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplit_terminator(self, pat) + RSplitTerminator(self.split_terminator(pat).0) } /// An iterator over substrings of the given string slice, separated by a @@ -3690,7 +3316,10 @@ macro_rules! str_core_methods { () => { #[stable(feature = "rust1", since = "1.0.0")] #[inline] pub fn splitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> SplitN<'a, P> { - StrExt::splitn(self, n, pat) + SplitN(SplitNInternal { + iter: self.split(pat).0, + count: n, + }) } /// An iterator over substrings of this string slice, separated by a @@ -3740,7 +3369,7 @@ macro_rules! str_core_methods { () => { pub fn rsplitn<'a, P: Pattern<'a>>(&'a self, n: usize, pat: P) -> RSplitN<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rsplitn(self, n, pat) + RSplitN(self.splitn(n, pat).0) } /// An iterator over the disjoint matches of a pattern within the given string @@ -3779,7 +3408,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_matches", since = "1.2.0")] #[inline] pub fn matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> Matches<'a, P> { - StrExt::matches(self, pat) + Matches(MatchesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within this string slice, @@ -3818,7 +3447,7 @@ macro_rules! str_core_methods { () => { pub fn rmatches<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatches<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatches(self, pat) + RMatches(self.matches(pat).0) } /// An iterator over the disjoint matches of a pattern within this string @@ -3862,7 +3491,7 @@ macro_rules! str_core_methods { () => { #[stable(feature = "str_match_indices", since = "1.5.0")] #[inline] pub fn match_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> MatchIndices<'a, P> { - StrExt::match_indices(self, pat) + MatchIndices(MatchIndicesInternal(pat.into_searcher(self))) } /// An iterator over the disjoint matches of a pattern within `self`, @@ -3907,7 +3536,7 @@ macro_rules! str_core_methods { () => { pub fn rmatch_indices<'a, P: Pattern<'a>>(&'a self, pat: P) -> RMatchIndices<'a, P> where P::Searcher: ReverseSearcher<'a> { - StrExt::rmatch_indices(self, pat) + RMatchIndices(self.match_indices(pat).0) } /// Returns a string slice with leading and trailing whitespace removed. @@ -3926,7 +3555,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim(&self) -> &str { - StrExt::trim(self) + self.trim_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with leading whitespace removed. @@ -3962,7 +3591,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left(&self) -> &str { - StrExt::trim_left(self) + self.trim_left_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with trailing whitespace removed. @@ -3998,7 +3627,7 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_right(&self) -> &str { - StrExt::trim_right(self) + self.trim_right_matches(|c: char| c.is_whitespace()) } /// Returns a string slice with all prefixes and suffixes that match a @@ -4030,7 +3659,21 @@ macro_rules! str_core_methods { () => { pub fn trim_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: DoubleEndedSearcher<'a> { - StrExt::trim_matches(self, pat) + let mut i = 0; + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((a, b)) = matcher.next_reject() { + i = a; + j = b; // Remember earliest known match, correct it below if + // last match is different + } + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, j) + } } /// Returns a string slice with all prefixes that match a pattern @@ -4061,7 +3704,15 @@ macro_rules! str_core_methods { () => { /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn trim_left_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str { - StrExt::trim_left_matches(self, pat) + let mut i = self.len(); + let mut matcher = pat.into_searcher(self); + if let Some((a, _)) = matcher.next_reject() { + i = a; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(i, self.len()) + } } /// Returns a string slice with all suffixes that match a pattern @@ -4100,7 +3751,15 @@ macro_rules! str_core_methods { () => { pub fn trim_right_matches<'a, P: Pattern<'a>>(&'a self, pat: P) -> &'a str where P::Searcher: ReverseSearcher<'a> { - StrExt::trim_right_matches(self, pat) + let mut j = 0; + let mut matcher = pat.into_searcher(self); + if let Some((_, b)) = matcher.next_reject_back() { + j = b; + } + unsafe { + // Searcher is known to return valid indices + self.slice_unchecked(0, j) + } } /// Parses this string slice into another type. @@ -4150,7 +3809,7 @@ macro_rules! str_core_methods { () => { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn parse(&self) -> Result { - StrExt::parse(self) + FromStr::from_str(self) } /// Checks if all characters in this string are within the ASCII range. @@ -4220,16 +3879,8 @@ macro_rules! str_core_methods { () => { let me = unsafe { self.as_bytes_mut() }; me.make_ascii_lowercase() } -}} - -#[lang = "str"] -#[cfg(not(test))] -#[cfg(not(stage0))] -impl str { - str_core_methods!(); } - #[stable(feature = "rust1", since = "1.0.0")] impl AsRef<[u8]> for str { #[inline] @@ -4332,17 +3983,6 @@ pub struct EncodeUtf16<'a> { extra: u16, } -// FIXME: remove (inline) this method -// when updating to a bootstrap compiler that has the new lang items. -// For grepping purpose: #[cfg(stage0)] -impl<'a> EncodeUtf16<'a> { - #[unstable(feature = "core_str_ext", issue = "32110")] - #[doc(hidden)] - pub fn new(s: &'a str) -> Self { - EncodeUtf16 { chars: s.chars(), extra: 0 } - } -} - #[stable(feature = "collection_debug", since = "1.17.0")] impl<'a> fmt::Debug for EncodeUtf16<'a> { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 7fb4b503c01..8c481338945 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -41,7 +41,6 @@ #![feature(try_from)] #![feature(try_trait)] #![feature(exact_chunks)] -#![cfg_attr(stage0, feature(atomic_nand))] #![feature(reverse_bits)] #![feature(inclusive_range_methods)] #![feature(iterator_find_map)] diff --git a/src/libcore/tests/num/uint_macros.rs b/src/libcore/tests/num/uint_macros.rs index 257f6ea20d4..ca6906f7310 100644 --- a/src/libcore/tests/num/uint_macros.rs +++ b/src/libcore/tests/num/uint_macros.rs @@ -98,7 +98,6 @@ mod tests { } #[test] - #[cfg(not(stage0))] fn test_reverse_bits() { assert_eq!(A.reverse_bits().reverse_bits(), A); assert_eq!(B.reverse_bits().reverse_bits(), B); diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index ac6ff6831ad..bbd684982fa 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -46,7 +46,6 @@ #![feature(core_intrinsics)] #![feature(drain_filter)] #![feature(entry_or_default)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(iterator_find_map)] diff --git a/src/librustc_mir/borrow_check/nll/mod.rs b/src/librustc_mir/borrow_check/nll/mod.rs index 0b1729294d8..a162ef36a60 100644 --- a/src/librustc_mir/borrow_check/nll/mod.rs +++ b/src/librustc_mir/borrow_check/nll/mod.rs @@ -240,11 +240,11 @@ fn dump_mir_results<'a, 'gcx, 'tcx>( }); // Also dump the inference graph constraints as a graphviz file. - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = pretty::create_dump_file(infcx.tcx, "regioncx.dot", None, "nll", &0, source)?; regioncx.dump_graphviz(&mut file)?; - }}; + }; } fn dump_annotation<'a, 'gcx, 'tcx>( diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index ecced1b8168..d9b6c406e20 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -24,7 +24,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(decl_macro)] -#![cfg_attr(stage0, feature(dyn_trait))] #![feature(fs_read_write)] #![feature(macro_vis_matcher)] #![feature(exhaustive_patterns)] @@ -34,7 +33,7 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(specialization)] -#![cfg_attr(stage0, feature(try_trait))] +#![feature(try_trait)] extern crate arena; #[macro_use] @@ -54,16 +53,6 @@ extern crate log_settings; extern crate rustc_apfloat; extern crate byteorder; -#[cfg(stage0)] -macro_rules! do_catch { - ($t:expr) => { (|| ::std::ops::Try::from_ok($t) )() } -} - -#[cfg(not(stage0))] -macro_rules! do_catch { - ($t:expr) => { do catch { $t } } -} - mod diagnostics; mod borrow_check; diff --git a/src/librustc_mir/util/pretty.rs b/src/librustc_mir/util/pretty.rs index 9d74ad0830f..9e1ce9b2851 100644 --- a/src/librustc_mir/util/pretty.rs +++ b/src/librustc_mir/util/pretty.rs @@ -137,7 +137,7 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( ) where F: FnMut(PassWhere, &mut dyn Write) -> io::Result<()>, { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "mir", pass_num, pass_name, disambiguator, source)?; writeln!(file, "// MIR for `{}`", node_path)?; writeln!(file, "// source = {:?}", source)?; @@ -150,14 +150,14 @@ fn dump_matched_mir_node<'a, 'gcx, 'tcx, F>( extra_data(PassWhere::BeforeCFG, &mut file)?; write_mir_fn(tcx, source, mir, &mut extra_data, &mut file)?; extra_data(PassWhere::AfterCFG, &mut file)?; - }}; + }; if tcx.sess.opts.debugging_opts.dump_mir_graphviz { - let _: io::Result<()> = do_catch! {{ + let _: io::Result<()> = do catch { let mut file = create_dump_file(tcx, "dot", pass_num, pass_name, disambiguator, source)?; write_mir_fn_graphviz(tcx, source.def_id, mir, &mut file)?; - }}; + }; } } diff --git a/src/librustc_typeck/lib.rs b/src/librustc_typeck/lib.rs index 350b53a406b..ef79517d06a 100644 --- a/src/librustc_typeck/lib.rs +++ b/src/librustc_typeck/lib.rs @@ -71,8 +71,6 @@ This API is completely unstable and subject to change. #![allow(non_camel_case_types)] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(box_patterns)] #![feature(box_syntax)] #![feature(crate_visibility_modifier)] diff --git a/src/librustdoc/lib.rs b/src/librustdoc/lib.rs index a14a27d5d61..e4b80b13aec 100644 --- a/src/librustdoc/lib.rs +++ b/src/librustdoc/lib.rs @@ -13,8 +13,6 @@ html_root_url = "https://doc.rust-lang.org/nightly/", html_playground_url = "https://play.rust-lang.org/")] -#![cfg_attr(stage0, feature(dyn_trait))] - #![feature(ascii_ctype)] #![feature(rustc_private)] #![feature(box_patterns)] diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index a8578404467..78d3d6d5e60 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -17,7 +17,6 @@ #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; -#[cfg(not(stage0))] #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] @@ -43,13 +42,6 @@ pub mod __default_lib_allocator { System.alloc(layout) as *mut u8 } - #[cfg(stage0)] - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_oom() -> ! { - super::oom() - } - #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, @@ -74,57 +66,4 @@ pub mod __default_lib_allocator { let layout = Layout::from_size_align_unchecked(size, align); System.alloc_zeroed(layout) as *mut u8 } - - #[cfg(stage0)] - pub mod stage0 { - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_usable_size(_layout: *const u8, - _min: *mut usize, - _max: *mut usize) { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_alloc_excess(_size: usize, - _align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_realloc_excess(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize, - _excess: *mut usize, - _err: *mut u8) -> *mut u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_grow_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - #[no_mangle] - #[rustc_std_internal_symbol] - pub unsafe extern fn __rdl_shrink_in_place(_ptr: *mut u8, - _old_size: usize, - _old_align: usize, - _new_size: usize, - _new_align: usize) -> u8 { - unimplemented!() - } - - } } diff --git a/src/libstd/f32.rs b/src/libstd/f32.rs index 7314d32b020..ae30321f46d 100644 --- a/src/libstd/f32.rs +++ b/src/libstd/f32.rs @@ -18,15 +18,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; #[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f32::{MIN, MIN_POSITIVE, MAX}; pub use core::f32::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f32")] -#[cfg_attr(not(stage0), lang = "f32_runtime")] +#[lang = "f32_runtime"] impl f32 { - #[cfg(stage0)] - f32_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/f64.rs b/src/libstd/f64.rs index 75edba8979f..7950d434b77 100644 --- a/src/libstd/f64.rs +++ b/src/libstd/f64.rs @@ -18,15 +18,9 @@ #![stable(feature = "rust1", since = "1.0.0")] #![allow(missing_docs)] -#[cfg(not(test))] -#[cfg(stage0)] -use core::num::Float; #[cfg(not(test))] use intrinsics; #[cfg(not(test))] -#[cfg(stage0)] -use num::FpCategory; -#[cfg(not(test))] use sys::cmath; #[stable(feature = "rust1", since = "1.0.0")] @@ -41,12 +35,8 @@ pub use core::f64::{MIN, MIN_POSITIVE, MAX}; pub use core::f64::consts; #[cfg(not(test))] -#[cfg_attr(stage0, lang = "f64")] -#[cfg_attr(not(stage0), lang = "f64_runtime")] +#[lang = "f64_runtime"] impl f64 { - #[cfg(stage0)] - f64_core_methods!(); - /// Returns the largest integer less than or equal to a number. /// /// # Examples diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 9cdc6a21622..f7d06852f27 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -252,7 +252,6 @@ #![feature(collections_range)] #![feature(compiler_builtins_lib)] #![feature(const_fn)] -#![cfg_attr(stage0, feature(core_float))] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] #![feature(exact_size_is_empty)] @@ -260,10 +259,8 @@ #![feature(fs_read_write)] #![feature(fixed_size_array)] #![feature(float_from_str_radix)] -#![cfg_attr(stage0, feature(float_internals))] #![feature(fn_traits)] #![feature(fnbox)] -#![cfg_attr(stage0, feature(generic_param_attrs))] #![feature(hashmap_internals)] #![feature(heap_api)] #![feature(int_error_internals)] @@ -319,6 +316,7 @@ #![cfg_attr(test, feature(update_panic_count))] #![cfg_attr(windows, feature(used))] #![feature(doc_alias)] +#![feature(float_internals)] #![default_lib_allocator] @@ -364,11 +362,6 @@ extern crate libc; #[allow(unused_extern_crates)] extern crate unwind; -// compiler-rt intrinsics -#[doc(masked)] -#[cfg(stage0)] -extern crate compiler_builtins; - // During testing, this crate is not actually the "real" std library, but rather // it links to the real std library, which was compiled from this same source // code. So any lang items std defines are conditionally excluded (or else they diff --git a/src/libsyntax_ext/asm.rs b/src/libsyntax_ext/asm.rs index 369c5b1ff60..dd8f79d20ab 100644 --- a/src/libsyntax_ext/asm.rs +++ b/src/libsyntax_ext/asm.rs @@ -45,17 +45,6 @@ impl State { } } -macro_rules! span_err_if_not_stage0 { - ($cx:expr, $sp:expr, $code:ident, $text:tt) => { - #[cfg(not(stage0))] { - span_err!($cx, $sp, $code, $text) - } - #[cfg(stage0)] { - $cx.span_err($sp, $text) - } - } -} - const OPTIONS: &'static [&'static str] = &["volatile", "alignstack", "intel"]; pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, @@ -100,7 +89,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if asm_str_style.is_some() { // If we already have a string with instructions, // ending up in Asm state again is an error. - span_err_if_not_stage0!(cx, sp, E0660, "malformed inline assembly"); + span_err!(cx, sp, E0660, "malformed inline assembly"); return DummyResult::expr(sp); } // Nested parser, stop before the first colon (see above). @@ -153,7 +142,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, Some(Symbol::intern(&format!("={}", ch.as_str()))) } _ => { - span_err_if_not_stage0!(cx, span, E0661, + span_err!(cx, span, E0661, "output operand constraint lacks '=' or '+'"); None } @@ -179,10 +168,10 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, let (constraint, _str_style) = panictry!(p.parse_str()); if constraint.as_str().starts_with("=") { - span_err_if_not_stage0!(cx, p.prev_span, E0662, + span_err!(cx, p.prev_span, E0662, "input operand constraint contains '='"); } else if constraint.as_str().starts_with("+") { - span_err_if_not_stage0!(cx, p.prev_span, E0663, + span_err!(cx, p.prev_span, E0663, "input operand constraint contains '+'"); } @@ -205,7 +194,7 @@ pub fn expand_asm<'cx>(cx: &'cx mut ExtCtxt, if OPTIONS.iter().any(|&opt| s == opt) { cx.span_warn(p.prev_span, "expected a clobber, found an option"); } else if s.as_str().starts_with("{") || s.as_str().ends_with("}") { - span_err_if_not_stage0!(cx, p.prev_span, E0664, + span_err!(cx, p.prev_span, E0664, "clobber should not be surrounded by braces"); } diff --git a/src/libsyntax_ext/lib.rs b/src/libsyntax_ext/lib.rs index b6721dd28f3..e100ef29225 100644 --- a/src/libsyntax_ext/lib.rs +++ b/src/libsyntax_ext/lib.rs @@ -18,7 +18,7 @@ #![feature(decl_macro)] #![feature(str_escape)] -#![cfg_attr(not(stage0), feature(rustc_diagnostic_macros))] +#![feature(rustc_diagnostic_macros)] extern crate fmt_macros; #[macro_use] @@ -29,7 +29,6 @@ extern crate rustc_data_structures; extern crate rustc_errors as errors; extern crate rustc_target; -#[cfg(not(stage0))] mod diagnostics; mod assert; diff --git a/src/rtstartup/rsbegin.rs b/src/rtstartup/rsbegin.rs index 7d5581bb774..8ff401164c1 100644 --- a/src/rtstartup/rsbegin.rs +++ b/src/rtstartup/rsbegin.rs @@ -23,7 +23,7 @@ // of other runtime components (registered via yet another special image section). #![feature(no_core, lang_items, optin_builtin_traits)] -#![crate_type="rlib"] +#![crate_type = "rlib"] #![no_core] #![allow(non_camel_case_types)] @@ -43,7 +43,7 @@ pub unsafe fn drop_in_place(to_drop: *mut T) { drop_in_place(to_drop); } -#[cfg(all(target_os="windows", target_arch = "x86", target_env="gnu"))] +#[cfg(all(target_os = "windows", target_arch = "x86", target_env = "gnu"))] pub mod eh_frames { #[no_mangle] #[link_section = ".eh_frame"] @@ -54,6 +54,21 @@ pub mod eh_frames { // This is defined as `struct object` in $GCC/libgcc/unwind-dw2-fde.h. static mut OBJ: [isize; 6] = [0; 6]; + macro_rules! impl_copy { + ($($t:ty)*) => { + $( + impl ::Copy for $t {} + )* + } + } + + impl_copy! { + usize u8 u16 u32 u64 u128 + isize i8 i16 i32 i64 i128 + f32 f64 + bool char + } + // Unwind info registration/deregistration routines. // See the docs of `unwind` module in libstd. extern "C" { @@ -63,14 +78,18 @@ pub mod eh_frames { unsafe fn init() { // register unwind info on module startup - rust_eh_register_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_register_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } unsafe fn uninit() { // unregister on shutdown - rust_eh_unregister_frames(&__EH_FRAME_BEGIN__ as *const u8, - &mut OBJ as *mut _ as *mut u8); + rust_eh_unregister_frames( + &__EH_FRAME_BEGIN__ as *const u8, + &mut OBJ as *mut _ as *mut u8, + ); } // MSVC-specific init/uninit routine registration diff --git a/src/stage0.txt b/src/stage0.txt index a5ad2b315a1..435cfd2f6db 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2018-04-24 +date: 2018-05-10 rustc: beta cargo: beta -- cgit 1.4.1-3-g733a5 From edad2eff0c0587cd0b9d4b46f11579ef43388be8 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 14 May 2018 23:25:22 +0100 Subject: Stabilise inclusive_range_methods --- src/liballoc/tests/lib.rs | 1 - src/libcore/ops/range.rs | 13 +++---------- src/libcore/tests/lib.rs | 1 - src/librustc/lib.rs | 1 - src/librustc_codegen_llvm/lib.rs | 1 - src/librustc_mir/lib.rs | 1 - src/librustc_target/lib.rs | 1 - 7 files changed, 3 insertions(+), 16 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 1c8ff316e55..081c473768f 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -25,7 +25,6 @@ #![feature(try_reserve)] #![feature(unboxed_closures)] #![feature(exact_chunks)] -#![feature(inclusive_range_methods)] extern crate alloc_system; extern crate core; diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 1f84631ada3..70da5fcda00 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -318,8 +318,6 @@ impl> RangeTo { /// # Examples /// /// ``` -/// #![feature(inclusive_range_methods)] -/// /// assert_eq!((3..=5), std::ops::RangeInclusive::new(3, 5)); /// assert_eq!(3 + 4 + 5, (3..=5).sum()); /// @@ -345,12 +343,11 @@ impl RangeInclusive { /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] /// use std::ops::RangeInclusive; /// /// assert_eq!(3..=5, RangeInclusive::new(3, 5)); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub const fn new(start: Idx, end: Idx) -> Self { Self { start, end } @@ -369,11 +366,9 @@ impl RangeInclusive { /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] - /// /// assert_eq!((3..=5).start(), &3); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub fn start(&self) -> &Idx { &self.start @@ -392,11 +387,9 @@ impl RangeInclusive { /// # Examples /// /// ``` - /// #![feature(inclusive_range_methods)] - /// /// assert_eq!((3..=5).end(), &5); /// ``` - #[unstable(feature = "inclusive_range_methods", issue = "49022")] + #[stable(feature = "inclusive_range_methods", since = "1.27.0")] #[inline] pub fn end(&self) -> &Idx { &self.end diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 8c481338945..cd6b5c6a4ad 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -42,7 +42,6 @@ #![feature(try_trait)] #![feature(exact_chunks)] #![feature(reverse_bits)] -#![feature(inclusive_range_methods)] #![feature(iterator_find_map)] #![feature(slice_internals)] diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index bbd684982fa..1d53a305193 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -68,7 +68,6 @@ #![feature(trusted_len)] #![feature(catch_expr)] #![feature(test)] -#![feature(inclusive_range_methods)] #![recursion_limit="512"] diff --git a/src/librustc_codegen_llvm/lib.rs b/src/librustc_codegen_llvm/lib.rs index bd053da4bd3..0b0bab96dfd 100644 --- a/src/librustc_codegen_llvm/lib.rs +++ b/src/librustc_codegen_llvm/lib.rs @@ -29,7 +29,6 @@ #![feature(rustc_diagnostic_macros)] #![feature(slice_sort_by_cached_key)] #![feature(optin_builtin_traits)] -#![feature(inclusive_range_methods)] use rustc::dep_graph::WorkProduct; use syntax_pos::symbol::Symbol; diff --git a/src/librustc_mir/lib.rs b/src/librustc_mir/lib.rs index d9b6c406e20..3bf9453fb51 100644 --- a/src/librustc_mir/lib.rs +++ b/src/librustc_mir/lib.rs @@ -29,7 +29,6 @@ Rust MIR: a lowered representation of Rust. Also: an experiment! #![feature(exhaustive_patterns)] #![feature(range_contains)] #![feature(rustc_diagnostic_macros)] -#![feature(inclusive_range_methods)] #![feature(crate_visibility_modifier)] #![feature(never_type)] #![feature(specialization)] diff --git a/src/librustc_target/lib.rs b/src/librustc_target/lib.rs index 45f2ee13bbd..8f491157439 100644 --- a/src/librustc_target/lib.rs +++ b/src/librustc_target/lib.rs @@ -29,7 +29,6 @@ #![feature(const_fn)] #![feature(fs_read_write)] #![feature(inclusive_range)] -#![feature(inclusive_range_methods)] #![feature(slice_patterns)] #[macro_use] -- cgit 1.4.1-3-g733a5 From 26d62f55a40db77c6bca85ad56a44aee898096a5 Mon Sep 17 00:00:00 2001 From: Stjepan Glavina Date: Mon, 21 May 2018 17:59:20 +0200 Subject: Stabilize feature from_ref --- src/liballoc/slice.rs | 4 ++-- src/libcore/slice/mod.rs | 6 +++--- src/librustc/mir/mod.rs | 2 +- 3 files changed, 6 insertions(+), 6 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 4427ac004f9..161493f3892 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -119,8 +119,8 @@ pub use core::slice::{SplitN, RSplitN, SplitNMut, RSplitNMut}; pub use core::slice::{RSplit, RSplitMut}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::slice::{from_raw_parts, from_raw_parts_mut}; -#[unstable(feature = "from_ref", issue = "45703")] -pub use core::slice::{from_ref, from_ref_mut}; +#[stable(feature = "from_ref", since = "1.28.0")] +pub use core::slice::{from_ref, from_mut}; #[unstable(feature = "slice_get_slice", issue = "35729")] pub use core::slice::SliceIndex; #[unstable(feature = "exact_chunks", issue = "47115")] diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index 3b19a401859..6278da85e9b 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -3874,7 +3874,7 @@ pub unsafe fn from_raw_parts_mut<'a, T>(p: *mut T, len: usize) -> &'a mut [T] { } /// Converts a reference to T into a slice of length 1 (without copying). -#[unstable(feature = "from_ref", issue = "45703")] +#[stable(feature = "from_ref", since = "1.28.0")] pub fn from_ref(s: &T) -> &[T] { unsafe { from_raw_parts(s, 1) @@ -3882,8 +3882,8 @@ pub fn from_ref(s: &T) -> &[T] { } /// Converts a reference to T into a slice of length 1 (without copying). -#[unstable(feature = "from_ref", issue = "45703")] -pub fn from_ref_mut(s: &mut T) -> &mut [T] { +#[stable(feature = "from_ref", since = "1.28.0")] +pub fn from_mut(s: &mut T) -> &mut [T] { unsafe { from_raw_parts_mut(s, 1) } diff --git a/src/librustc/mir/mod.rs b/src/librustc/mir/mod.rs index f42f876510d..246377b7b57 100644 --- a/src/librustc/mir/mod.rs +++ b/src/librustc/mir/mod.rs @@ -948,7 +948,7 @@ impl<'tcx> TerminatorKind<'tcx> { Drop { target: ref mut t, unwind: Some(ref mut u), .. } | Assert { target: ref mut t, cleanup: Some(ref mut u), .. } | FalseUnwind { real_target: ref mut t, unwind: Some(ref mut u) } => { - Some(t).into_iter().chain(slice::from_ref_mut(u)) + Some(t).into_iter().chain(slice::from_mut(u)) } SwitchInt { ref mut targets, .. } => { None.into_iter().chain(&mut targets[..]) -- cgit 1.4.1-3-g733a5 From 8c89e7f3d58ff110aa4de64aef8ef29f78ebf456 Mon Sep 17 00:00:00 2001 From: varkor Date: Wed, 16 May 2018 23:20:22 +0100 Subject: Make {char, str}::escape_debug and impl Debug for {char, str} consistent --- src/liballoc/tests/str.rs | 1 + src/libcore/fmt/mod.rs | 10 ++-------- src/libcore/tests/char.rs | 9 +-------- 3 files changed, 4 insertions(+), 16 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 1a47e5433ea..2f38c8b3ae2 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -999,6 +999,7 @@ fn test_escape_debug() { assert_eq!("\u{10000}\u{10ffff}".escape_debug(), "\u{10000}\\u{10ffff}"); assert_eq!("ab\u{200b}".escape_debug(), "ab\\u{200b}"); assert_eq!("\u{10d4ea}\r".escape_debug(), "\\u{10d4ea}\\r"); + assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug(), "\\u{301}a\\u{301}bé\\u{e000}"); } #[test] diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 1cfde513102..5820fe58932 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1844,14 +1844,8 @@ impl Display for str { impl Debug for char { fn fmt(&self, f: &mut Formatter) -> Result { f.write_char('\'')?; - if self.is_nonspacing_mark() { - for c in self.escape_unicode() { - f.write_char(c)? - } - } else { - for c in self.escape_debug() { - f.write_char(c)? - } + for c in self.escape_debug() { + f.write_char(c)? } f.write_char('\'') } diff --git a/src/libcore/tests/char.rs b/src/libcore/tests/char.rs index 76f28d493ab..d19e3b52769 100644 --- a/src/libcore/tests/char.rs +++ b/src/libcore/tests/char.rs @@ -181,19 +181,12 @@ fn test_escape_debug() { assert_eq!(string('\u{ff}'), "\u{ff}"); assert_eq!(string('\u{11b}'), "\u{11b}"); assert_eq!(string('\u{1d4b6}'), "\u{1d4b6}"); + assert_eq!(string('\u{301}'), "'\\u{301}'"); // combining character assert_eq!(string('\u{200b}'),"\\u{200b}"); // zero width space assert_eq!(string('\u{e000}'), "\\u{e000}"); // private use 1 assert_eq!(string('\u{100000}'), "\\u{100000}"); // private use 2 } -#[test] -fn test_debug() { - assert_eq!(format!("{:?}", 'a'), "'a'"); // ASCII character - assert_eq!(format!("{:?}", 'é'), "'é'"); // printable character - assert_eq!(format!("{:?}", '\u{301}'), "'\\u{301}'"); // combining character - assert_eq!(format!("{:?}", '\u{e000}'), "'\\u{e000}'"); // private use 1 -} - #[test] fn test_escape_default() { fn string(c: char) -> String { -- cgit 1.4.1-3-g733a5 From c51f00280205d476651ff9f9a46cff6645b411a2 Mon Sep 17 00:00:00 2001 From: varkor Date: Thu, 17 May 2018 10:45:34 +0100 Subject: Only escape extended grapheme characters in the first position --- src/liballoc/str.rs | 5 ++++- src/liballoc/tests/str.rs | 2 +- src/libcore/char/methods.rs | 34 ++++++++++++++++++++++------------ src/libcore/tests/char.rs | 2 +- 4 files changed, 28 insertions(+), 15 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index c10c0a69433..8af14d3c698 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -372,12 +372,15 @@ impl str { /// Escapes each char in `s` with [`char::escape_debug`]. /// + /// Note: only extended grapheme codepoints that begin the string will be + /// escaped. + /// /// [`char::escape_debug`]: primitive.char.html#method.escape_debug #[unstable(feature = "str_escape", reason = "return type may change to be an iterator", issue = "27791")] pub fn escape_debug(&self) -> String { - self.chars().flat_map(|c| c.escape_debug()).collect() + self.chars().enumerate().flat_map(|(i, c)| c.escape_debug_ext(i == 0)).collect() } /// Escapes each char in `s` with [`char::escape_default`]. diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 2f38c8b3ae2..84c97abcbc2 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -999,7 +999,7 @@ fn test_escape_debug() { assert_eq!("\u{10000}\u{10ffff}".escape_debug(), "\u{10000}\\u{10ffff}"); assert_eq!("ab\u{200b}".escape_debug(), "ab\\u{200b}"); assert_eq!("\u{10d4ea}\r".escape_debug(), "\\u{10d4ea}\\r"); - assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug(), "\\u{301}a\\u{301}bé\\u{e000}"); + assert_eq!("\u{301}a\u{301}bé\u{e000}".escape_debug(), "\\u{301}a\u{301}bé\\u{e000}"); } #[test] diff --git a/src/libcore/char/methods.rs b/src/libcore/char/methods.rs index bf7772492e5..f6b201fe06d 100644 --- a/src/libcore/char/methods.rs +++ b/src/libcore/char/methods.rs @@ -187,6 +187,27 @@ impl char { } } + /// An extended version of `escape_debug` that optionally permits escaping + /// Extended Grapheme codepoints. This allows us to format characters like + /// nonspacing marks better when they're at the start of a string. + #[doc(hidden)] + #[unstable(feature = "str_internals", issue = "0")] + #[inline] + pub fn escape_debug_ext(self, escape_grapheme_extended: bool) -> EscapeDebug { + let init_state = match self { + '\t' => EscapeDefaultState::Backslash('t'), + '\r' => EscapeDefaultState::Backslash('r'), + '\n' => EscapeDefaultState::Backslash('n'), + '\\' | '\'' | '"' => EscapeDefaultState::Backslash(self), + _ if escape_grapheme_extended && self.is_grapheme_extended() => { + EscapeDefaultState::Unicode(self.escape_unicode()) + } + _ if is_printable(self) => EscapeDefaultState::Char(self), + _ => EscapeDefaultState::Unicode(self.escape_unicode()), + }; + EscapeDebug(EscapeDefault { state: init_state }) + } + /// Returns an iterator that yields the literal escape code of a character /// as `char`s. /// @@ -224,18 +245,7 @@ impl char { #[stable(feature = "char_escape_debug", since = "1.20.0")] #[inline] pub fn escape_debug(self) -> EscapeDebug { - let init_state = match self { - '\t' => EscapeDefaultState::Backslash('t'), - '\r' => EscapeDefaultState::Backslash('r'), - '\n' => EscapeDefaultState::Backslash('n'), - '\\' | '\'' | '"' => EscapeDefaultState::Backslash(self), - _ if self.is_grapheme_extended() => { - EscapeDefaultState::Unicode(self.escape_unicode()) - } - _ if is_printable(self) => EscapeDefaultState::Char(self), - _ => EscapeDefaultState::Unicode(self.escape_unicode()), - }; - EscapeDebug(EscapeDefault { state: init_state }) + self.escape_debug_ext(true) } /// Returns an iterator that yields the literal escape code of a character diff --git a/src/libcore/tests/char.rs b/src/libcore/tests/char.rs index d19e3b52769..d2a9ed75be6 100644 --- a/src/libcore/tests/char.rs +++ b/src/libcore/tests/char.rs @@ -181,7 +181,7 @@ fn test_escape_debug() { assert_eq!(string('\u{ff}'), "\u{ff}"); assert_eq!(string('\u{11b}'), "\u{11b}"); assert_eq!(string('\u{1d4b6}'), "\u{1d4b6}"); - assert_eq!(string('\u{301}'), "'\\u{301}'"); // combining character + assert_eq!(string('\u{301}'), "\\u{301}"); // combining character assert_eq!(string('\u{200b}'),"\\u{200b}"); // zero width space assert_eq!(string('\u{e000}'), "\\u{e000}"); // private use 1 assert_eq!(string('\u{100000}'), "\\u{100000}"); // private use 2 -- cgit 1.4.1-3-g733a5 From 2fa22effb6e1dd3b3e2e587ec5fcabefe2eb3443 Mon Sep 17 00:00:00 2001 From: varkor Date: Mon, 21 May 2018 18:57:49 +0100 Subject: Avoid counting characters and add explanatory comment to test --- src/liballoc/str.rs | 8 +++++++- src/liballoc/tests/str.rs | 6 ++++++ src/libcore/unicode/unicode.py | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 8af14d3c698..823e56b64e3 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -380,7 +380,13 @@ impl str { reason = "return type may change to be an iterator", issue = "27791")] pub fn escape_debug(&self) -> String { - self.chars().enumerate().flat_map(|(i, c)| c.escape_debug_ext(i == 0)).collect() + let mut string = String::with_capacity(self.len()); + let mut chars = self.chars(); + if let Some(first) = chars.next() { + string.extend(first.escape_debug_ext(true)) + } + string.extend(chars.flat_map(|c| c.escape_debug_ext(false))); + string } /// Escapes each char in `s` with [`char::escape_default`]. diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 84c97abcbc2..d11bf5dc3e9 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -989,6 +989,12 @@ fn test_escape_unicode() { #[test] fn test_escape_debug() { + // Note that there are subtleties with the number of backslashes + // on the left- and right-hand sides. In particular, Unicode code points + // are usually escaped with two backslashes on the right-hand side, as + // they are escaped. However, when the character is unescaped (e.g. for + // printable characters), only a single backslash appears (as the character + // itself appears in the debug string). assert_eq!("abc".escape_debug(), "abc"); assert_eq!("a c".escape_debug(), "a c"); assert_eq!("éèê".escape_debug(), "éèê"); diff --git a/src/libcore/unicode/unicode.py b/src/libcore/unicode/unicode.py index 1da5878c4c6..07f873b13c0 100755 --- a/src/libcore/unicode/unicode.py +++ b/src/libcore/unicode/unicode.py @@ -21,7 +21,7 @@ # - UnicodeData.txt # # Since this should not require frequent updates, we just store this -# out-of-line and check the unicode.py file into git. +# out-of-line and check the tables.rs file into git. import fileinput, re, os, sys, operator, math, datetime -- cgit 1.4.1-3-g733a5 From a44abfdc29ee66ec1d51c2389405cbac479f35a7 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Tue, 22 May 2018 17:09:49 -0700 Subject: Make `Unpin` safe to implement --- src/liballoc/boxed.rs | 2 +- src/libcore/marker.rs | 2 +- src/libcore/mem.rs | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index a1567344235..a83ce7f379f 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -850,4 +850,4 @@ impl fmt::Pointer for PinBox { impl, U: ?Sized> CoerceUnsized> for PinBox {} #[unstable(feature = "pin", issue = "49150")] -unsafe impl Unpin for PinBox {} +impl Unpin for PinBox {} diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 6c8ee0eda11..d4a87b13f04 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -605,7 +605,7 @@ unsafe impl<'a, T: ?Sized> Freeze for &'a mut T {} /// /// [`PinMut`]: ../mem/struct.PinMut.html #[unstable(feature = "pin", issue = "49150")] -pub unsafe auto trait Unpin {} +pub auto trait Unpin {} /// Implementations of `Copy` for primitive types. /// diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 20445def634..116e56f4ae9 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1207,4 +1207,4 @@ impl<'a, T: ?Sized> fmt::Pointer for PinMut<'a, T> { impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for PinMut<'a, T> {} #[unstable(feature = "pin", issue = "49150")] -unsafe impl<'a, T: ?Sized> Unpin for PinMut<'a, T> {} +impl<'a, T: ?Sized> Unpin for PinMut<'a, T> {} -- cgit 1.4.1-3-g733a5 From 1440f300d848610b0cb798a735e2c75a94998aa9 Mon Sep 17 00:00:00 2001 From: Cory Sherman Date: Thu, 24 May 2018 04:39:35 -0700 Subject: stabilize RangeBounds collections_range #30877 rename RangeBounds::start() -> start_bound() rename RangeBounds::end() -> end_bound() --- src/liballoc/btree/map.rs | 6 +- src/liballoc/string.rs | 8 +- src/liballoc/vec.rs | 4 +- src/liballoc/vec_deque.rs | 4 +- src/libcore/ops/range.rs | 138 +++++++++++------------------ src/librustc_data_structures/array_vec.rs | 4 +- src/librustc_data_structures/sorted_map.rs | 4 +- 7 files changed, 68 insertions(+), 100 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index bb2c68a27ba..28c42144b2a 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -1834,7 +1834,7 @@ fn range_search>( Handle, marker::Edge>) where Q: Ord, K: Borrow { - match (range.start(), range.end()) { + match (range.start_bound(), range.end_bound()) { (Excluded(s), Excluded(e)) if s==e => panic!("range start and end are equal and excluded in BTreeMap"), (Included(s), Included(e)) | @@ -1852,7 +1852,7 @@ fn range_search>( let mut diverged = false; loop { - let min_edge = match (min_found, range.start()) { + let min_edge = match (min_found, range.start_bound()) { (false, Included(key)) => match search::search_linear(&min_node, key) { (i, true) => { min_found = true; i }, (i, false) => i, @@ -1866,7 +1866,7 @@ fn range_search>( (true, Excluded(_)) => 0, }; - let max_edge = match (max_found, range.end()) { + let max_edge = match (max_found, range.end_bound()) { (false, Included(key)) => match search::search_linear(&max_node, key) { (i, true) => { max_found = true; i+1 }, (i, false) => i, diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index 449e3152d8f..a988b6a26d9 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1493,12 +1493,12 @@ impl String { // Because the range removal happens in Drop, if the Drain iterator is leaked, // the removal will not happen. let len = self.len(); - let start = match range.start() { + let start = match range.start_bound() { Included(&n) => n, Excluded(&n) => n + 1, Unbounded => 0, }; - let end = match range.end() { + let end = match range.end_bound() { Included(&n) => n + 1, Excluded(&n) => n, Unbounded => len, @@ -1551,12 +1551,12 @@ impl String { // Replace_range does not have the memory safety issues of a vector Splice. // of the vector version. The data is just plain bytes. - match range.start() { + match range.start_bound() { Included(&n) => assert!(self.is_char_boundary(n)), Excluded(&n) => assert!(self.is_char_boundary(n + 1)), Unbounded => {}, }; - match range.end() { + match range.end_bound() { Included(&n) => assert!(self.is_char_boundary(n + 1)), Excluded(&n) => assert!(self.is_char_boundary(n)), Unbounded => {}, diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index c48c64a8bef..b5739e1a825 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1166,12 +1166,12 @@ impl Vec { // the hole, and the vector length is restored to the new length. // let len = self.len(); - let start = match range.start() { + let start = match range.start_bound() { Included(&n) => n, Excluded(&n) => n + 1, Unbounded => 0, }; - let end = match range.end() { + let end = match range.end_bound() { Included(&n) => n + 1, Excluded(&n) => n, Unbounded => len, diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs index ff82b3a469c..e917a65c9c5 100644 --- a/src/liballoc/vec_deque.rs +++ b/src/liballoc/vec_deque.rs @@ -980,12 +980,12 @@ impl VecDeque { // and the head/tail values will be restored correctly. // let len = self.len(); - let start = match range.start() { + let start = match range.start_bound() { Included(&n) => n, Excluded(&n) => n + 1, Unbounded => 0, }; - let end = match range.end() { + let end = match range.end_bound() { Included(&n) => n + 1, Excluded(&n) => n, Unbounded => len, diff --git a/src/libcore/ops/range.rs b/src/libcore/ops/range.rs index 7c6e2447bdb..01e279589da 100644 --- a/src/libcore/ops/range.rs +++ b/src/libcore/ops/range.rs @@ -588,14 +588,12 @@ impl> RangeToInclusive { /// `Bound`s are range endpoints: /// /// ``` -/// #![feature(collections_range)] -/// /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// -/// assert_eq!((..100).start(), Unbounded); -/// assert_eq!((1..12).start(), Included(&1)); -/// assert_eq!((1..12).end(), Excluded(&12)); +/// assert_eq!((..100).start_bound(), Unbounded); +/// assert_eq!((1..12).start_bound(), Included(&1)); +/// assert_eq!((1..12).end_bound(), Excluded(&12)); /// ``` /// /// Using a tuple of `Bound`s as an argument to [`BTreeMap::range`]. @@ -632,9 +630,7 @@ pub enum Bound { Unbounded, } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] /// `RangeBounds` is implemented by Rust's built-in range types, produced /// by range syntax like `..`, `a..`, `..b` or `c..d`. pub trait RangeBounds { @@ -645,17 +641,16 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// #![feature(collections_range)] - /// /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// - /// assert_eq!((..10).start(), Unbounded); - /// assert_eq!((3..10).start(), Included(&3)); + /// assert_eq!((..10).start_bound(), Unbounded); + /// assert_eq!((3..10).start_bound(), Included(&3)); /// # } /// ``` - fn start(&self) -> Bound<&T>; + #[stable(feature = "collections_range", since = "1.28.0")] + fn start_bound(&self) -> Bound<&T>; /// End index bound. /// @@ -664,17 +659,16 @@ pub trait RangeBounds { /// # Examples /// /// ``` - /// #![feature(collections_range)] - /// /// # fn main() { /// use std::ops::Bound::*; /// use std::ops::RangeBounds; /// - /// assert_eq!((3..).end(), Unbounded); - /// assert_eq!((3..10).end(), Excluded(&10)); + /// assert_eq!((3..).end_bound(), Unbounded); + /// assert_eq!((3..10).end_bound(), Excluded(&10)); /// # } /// ``` - fn end(&self) -> Bound<&T>; + #[stable(feature = "collections_range", since = "1.28.0")] + fn end_bound(&self) -> Bound<&T>; /// Returns `true` if `item` is contained in the range. @@ -699,13 +693,13 @@ pub trait RangeBounds { T: PartialOrd, U: ?Sized + PartialOrd, { - (match self.start() { + (match self.start_bound() { Included(ref start) => *start <= item, Excluded(ref start) => *start < item, Unbounded => true, }) && - (match self.end() { + (match self.end_bound() { Included(ref end) => item <= *end, Excluded(ref end) => item < *end, Unbounded => true, @@ -715,83 +709,69 @@ pub trait RangeBounds { use self::Bound::{Excluded, Included, Unbounded}; -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeFull { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Unbounded } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Unbounded } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeFrom { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(&self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Unbounded } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeTo { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Unbounded } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Excluded(&self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for Range { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(&self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Excluded(&self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeInclusive { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(&self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Included(&self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for RangeToInclusive { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Unbounded } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Included(&self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl RangeBounds for (Bound, Bound) { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { match *self { (Included(ref start), _) => Included(start), (Excluded(ref start), _) => Excluded(start), @@ -799,7 +779,7 @@ impl RangeBounds for (Bound, Bound) { } } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { match *self { (_, Included(ref end)) => Included(end), (_, Excluded(ref end)) => Excluded(end), @@ -808,75 +788,63 @@ impl RangeBounds for (Bound, Bound) { } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T: ?Sized + 'a> RangeBounds for (Bound<&'a T>, Bound<&'a T>) { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { self.0 } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { self.1 } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T> RangeBounds for RangeFrom<&'a T> { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Unbounded } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T> RangeBounds for RangeTo<&'a T> { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Unbounded } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Excluded(self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T> RangeBounds for Range<&'a T> { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Excluded(self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T> RangeBounds for RangeInclusive<&'a T> { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Included(self.start) } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Included(self.end) } } -#[unstable(feature = "collections_range", - reason = "might be replaced with `Into<_>` and a type containing two `Bound` values", - issue = "30877")] +#[stable(feature = "collections_range", since = "1.28.0")] impl<'a, T> RangeBounds for RangeToInclusive<&'a T> { - fn start(&self) -> Bound<&T> { + fn start_bound(&self) -> Bound<&T> { Unbounded } - fn end(&self) -> Bound<&T> { + fn end_bound(&self) -> Bound<&T> { Included(self.end) } } diff --git a/src/librustc_data_structures/array_vec.rs b/src/librustc_data_structures/array_vec.rs index 7b33ee40d8c..56bb9613242 100644 --- a/src/librustc_data_structures/array_vec.rs +++ b/src/librustc_data_structures/array_vec.rs @@ -119,12 +119,12 @@ impl ArrayVec { // the hole, and the vector length is restored to the new length. // let len = self.len(); - let start = match range.start() { + let start = match range.start_bound() { Included(&n) => n, Excluded(&n) => n + 1, Unbounded => 0, }; - let end = match range.end() { + let end = match range.end_bound() { Included(&n) => n + 1, Excluded(&n) => n, Unbounded => len, diff --git a/src/librustc_data_structures/sorted_map.rs b/src/librustc_data_structures/sorted_map.rs index e14bd33c82c..f7e7d6405fc 100644 --- a/src/librustc_data_structures/sorted_map.rs +++ b/src/librustc_data_structures/sorted_map.rs @@ -214,7 +214,7 @@ impl SortedMap { fn range_slice_indices(&self, range: R) -> (usize, usize) where R: RangeBounds { - let start = match range.start() { + let start = match range.start_bound() { Bound::Included(ref k) => { match self.lookup_index_for(k) { Ok(index) | Err(index) => index @@ -229,7 +229,7 @@ impl SortedMap { Bound::Unbounded => 0, }; - let end = match range.end() { + let end = match range.end_bound() { Bound::Included(ref k) => { match self.lookup_index_for(k) { Ok(index) => index + 1, -- cgit 1.4.1-3-g733a5 From f67453729c19b435686c94936d8145051e7f1284 Mon Sep 17 00:00:00 2001 From: Alex Crichton Date: Thu, 24 May 2018 12:03:05 -0700 Subject: std: Ensure OOM is classified as `nounwind` OOM can't unwind today, and historically it's been optimized as if it can't unwind. This accidentally regressed with recent changes to the OOM handler, so this commit adds in a codegen test to assert that everything gets optimized away after the OOM function is approrpiately classified as nounwind Closes #50925 --- src/liballoc/alloc.rs | 1 + src/librustc_codegen_llvm/attributes.rs | 29 ++++++++++++++++++++++++----- src/librustc_codegen_llvm/callee.rs | 11 ----------- src/test/codegen/vec-iter-collect-len.rs | 21 +++++++++++++++++++++ 4 files changed, 46 insertions(+), 16 deletions(-) create mode 100644 src/test/codegen/vec-iter-collect-len.rs (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 79607b06f94..4ae8fc649dd 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -133,6 +133,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } +#[rustc_allocator_nounwind] pub fn oom() -> ! { extern { #[lang = "oom"] diff --git a/src/librustc_codegen_llvm/attributes.rs b/src/librustc_codegen_llvm/attributes.rs index b64e102ba78..d6806e7afd3 100644 --- a/src/librustc_codegen_llvm/attributes.rs +++ b/src/librustc_codegen_llvm/attributes.rs @@ -20,7 +20,9 @@ use rustc::ty::TyCtxt; use rustc::ty::maps::Providers; use rustc_data_structures::sync::Lrc; use rustc_data_structures::fx::FxHashMap; +use rustc_target::spec::PanicStrategy; +use attributes; use llvm::{self, Attribute, ValueRef}; use llvm::AttributePlace::Function; use llvm_util; @@ -135,11 +137,28 @@ pub fn from_fn_attrs(cx: &CodegenCx, llfn: ValueRef, id: DefId) { Attribute::NoAlias.apply_llfn( llvm::AttributePlace::ReturnValue, llfn); } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { - unwind(llfn, true); - } - if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { - unwind(llfn, false); + + let can_unwind = if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::UNWIND) { + Some(true) + } else if codegen_fn_attrs.flags.contains(CodegenFnAttrFlags::RUSTC_ALLOCATOR_NOUNWIND) { + Some(false) + + // Perhaps questionable, but we assume that anything defined + // *in Rust code* may unwind. Foreign items like `extern "C" { + // fn foo(); }` are assumed not to unwind **unless** they have + // a `#[unwind]` attribute. + } else if !cx.tcx.is_foreign_item(id) { + Some(true) + } else { + None + }; + + match can_unwind { + Some(false) => attributes::unwind(llfn, false), + Some(true) if cx.tcx.sess.panic_strategy() == PanicStrategy::Unwind => { + attributes::unwind(llfn, true); + } + Some(true) | None => {} } let features = llvm_target_features(cx.tcx.sess) diff --git a/src/librustc_codegen_llvm/callee.rs b/src/librustc_codegen_llvm/callee.rs index a3dbc450ce7..2c01bd42cc7 100644 --- a/src/librustc_codegen_llvm/callee.rs +++ b/src/librustc_codegen_llvm/callee.rs @@ -26,7 +26,6 @@ use rustc::hir::def_id::DefId; use rustc::ty::{self, TypeFoldable}; use rustc::ty::layout::LayoutOf; use rustc::ty::subst::Substs; -use rustc_target::spec::PanicStrategy; /// Codegens a reference to a fn/method item, monomorphizing and /// inlining as it goes. @@ -102,16 +101,6 @@ pub fn get_fn<'a, 'tcx>(cx: &CodegenCx<'a, 'tcx>, let instance_def_id = instance.def_id(); - // Perhaps questionable, but we assume that anything defined - // *in Rust code* may unwind. Foreign items like `extern "C" { - // fn foo(); }` are assumed not to unwind **unless** they have - // a `#[unwind]` attribute. - if tcx.sess.panic_strategy() == PanicStrategy::Unwind { - if !tcx.is_foreign_item(instance_def_id) { - attributes::unwind(llfn, true); - } - } - // Apply an appropriate linkage/visibility value to our item that we // just declared. // diff --git a/src/test/codegen/vec-iter-collect-len.rs b/src/test/codegen/vec-iter-collect-len.rs new file mode 100644 index 00000000000..efb384d0afb --- /dev/null +++ b/src/test/codegen/vec-iter-collect-len.rs @@ -0,0 +1,21 @@ +// 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 or the MIT license +// , 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"] + +#[no_mangle] +pub fn get_len() -> usize { + // CHECK-LABEL: @get_len + // CHECK-NOT: call + // CHECK-NOT: invoke + [1, 2, 3].iter().collect::>().len() +} -- cgit 1.4.1-3-g733a5 From 91f7ae26b0b288f7c36fb52efa16e28d5b0c9d5f Mon Sep 17 00:00:00 2001 From: Martin Carton Date: Fri, 25 May 2018 22:55:33 +0200 Subject: Add inner links in documentation From [this SO question](https://stackoverflow.com/q/50518757/2733851) it looks like this page isn't really clear. I personally do think this page is quite clear, the only think I could think of was adding some references. --- src/liballoc/fmt.rs | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index b2c4582e840..a4e5373d907 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -340,7 +340,8 @@ //! //! ## Fill/Alignment //! -//! The fill character is provided normally in conjunction with the `width` +//! The fill character is provided normally in conjunction with the +//! [`width`](#width) //! parameter. This indicates that if the value being formatted is smaller than //! `width` some extra characters will be printed around it. The extra //! characters are specified by `fill`, and the alignment can be one of the @@ -388,7 +389,8 @@ //! padding specified by fill/alignment will be used to take up the required //! space. //! -//! The default fill/alignment for non-numerics is a space and left-aligned. The +//! The default [fill/alignment](#fillalignment) for non-numerics is a space and +//! left-aligned. The //! defaults for numeric formatters is also a space but with right-alignment. If //! the `0` flag is specified for numerics, then the implicit fill character is //! `0`. -- cgit 1.4.1-3-g733a5 From 0f4ef003ac1691d04f0ce519d1d78696689534aa Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Tue, 15 May 2018 09:56:46 +0900 Subject: Pass a `Layout` to `oom` As discussed in https://github.com/rust-lang/rust/issues/49668#issuecomment-384893456 and subsequent, there are use-cases where the OOM handler needs to know the size of the allocation that failed. The alignment might also be a cause for allocation failure, so providing it as well can be useful. --- src/liballoc/alloc.rs | 13 +-- src/liballoc/arc.rs | 2 +- src/liballoc/raw_vec.rs | 156 ++++++++++++++++-------------- src/liballoc/rc.rs | 2 +- src/libstd/alloc.rs | 4 +- src/libstd/collections/hash/map.rs | 39 ++++++-- src/libstd/collections/hash/table.rs | 43 +++++--- src/test/run-pass/allocator-alloc-one.rs | 4 +- src/test/run-pass/realloc-16687.rs | 6 +- src/test/run-pass/regions-mock-codegen.rs | 4 +- 10 files changed, 161 insertions(+), 112 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 4ae8fc649dd..8753c495737 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -115,7 +115,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr as *mut u8 } else { - oom() + oom(layout) } } } @@ -134,12 +134,13 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } #[rustc_allocator_nounwind] -pub fn oom() -> ! { - extern { +pub fn oom(layout: Layout) -> ! { + #[allow(improper_ctypes)] + extern "Rust" { #[lang = "oom"] - fn oom_impl() -> !; + fn oom_impl(layout: Layout) -> !; } - unsafe { oom_impl() } + unsafe { oom_impl(layout) } } #[cfg(test)] @@ -154,7 +155,7 @@ mod tests { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index d0950bff9ce..f7513248784 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -553,7 +553,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 5c6f6b22aae..07bb7f1a3eb 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -96,14 +96,15 @@ impl RawVec { NonNull::::dangling().as_opaque() } else { let align = mem::align_of::(); + let layout = Layout::from_size_align(alloc_size, align).unwrap(); let result = if zeroed { - a.alloc_zeroed(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc_zeroed(layout) } else { - a.alloc(Layout::from_size_align(alloc_size, align).unwrap()) + a.alloc(layout) }; match result { Ok(ptr) => ptr, - Err(_) => oom(), + Err(_) => oom(layout), } }; @@ -318,7 +319,7 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => oom(), + Err(_) => oom(Layout::from_size_align_unchecked(new_size, cur.align())), } } None => { @@ -327,7 +328,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => oom(), + Err(_) => oom(Layout::array::(new_cap).unwrap()), } } }; @@ -389,37 +390,7 @@ impl RawVec { pub fn try_reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they gave a bad `used_cap`. - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - // Nothing we can really do about these checks :( - let new_cap = used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } + self.reserve_internal(used_cap, needed_extra_cap, Fallible, Exact) } /// Ensures that the buffer contains at least enough space to hold @@ -443,9 +414,9 @@ impl RawVec { /// /// Aborts on OOM pub fn reserve_exact(&mut self, used_cap: usize, needed_extra_cap: usize) { - match self.try_reserve_exact(used_cap, needed_extra_cap) { + match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Exact) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => oom(), + Err(AllocErr) => unreachable!(), Ok(()) => { /* yay */ } } } @@ -467,37 +438,7 @@ impl RawVec { /// The same as `reserve`, but returns on errors instead of panicking or aborting. pub fn try_reserve(&mut self, used_cap: usize, needed_extra_cap: usize) -> Result<(), CollectionAllocErr> { - unsafe { - // NOTE: we don't early branch on ZSTs here because we want this - // to actually catch "asking for more than usize::MAX" in that case. - // If we make it past the first branch then we are guaranteed to - // panic. - - // Don't actually need any more capacity. - // Wrapping in case they give a bad `used_cap` - if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { - return Ok(()); - } - - let new_cap = self.amortized_new_size(used_cap, needed_extra_cap)?; - let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; - - // FIXME: may crash and burn on over-reserve - alloc_guard(new_layout.size())?; - - let res = match self.current_layout() { - Some(layout) => { - debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) - } - None => self.a.alloc(new_layout), - }; - - self.ptr = res?.cast().into(); - self.cap = new_cap; - - Ok(()) - } + self.reserve_internal(used_cap, needed_extra_cap, Fallible, Amortized) } /// Ensures that the buffer contains at least enough space to hold @@ -553,12 +494,12 @@ impl RawVec { /// # } /// ``` pub fn reserve(&mut self, used_cap: usize, needed_extra_cap: usize) { - match self.try_reserve(used_cap, needed_extra_cap) { + match self.reserve_internal(used_cap, needed_extra_cap, Infallible, Amortized) { Err(CapacityOverflow) => capacity_overflow(), - Err(AllocErr) => oom(), + Err(AllocErr) => unreachable!(), Ok(()) => { /* yay */ } - } - } + } + } /// Attempts to ensure that the buffer contains at least enough space to hold /// `used_cap + needed_extra_cap` elements. If it doesn't already have /// enough capacity, will reallocate in place enough space plus comfortable slack @@ -670,7 +611,7 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => oom(), + Err(_) => oom(Layout::from_size_align_unchecked(new_size, align)), } } self.cap = amount; @@ -678,6 +619,73 @@ impl RawVec { } } +enum Fallibility { + Fallible, + Infallible, +} + +use self::Fallibility::*; + +enum ReserveStrategy { + Exact, + Amortized, +} + +use self::ReserveStrategy::*; + +impl RawVec { + fn reserve_internal( + &mut self, + used_cap: usize, + needed_extra_cap: usize, + fallibility: Fallibility, + strategy: ReserveStrategy, + ) -> Result<(), CollectionAllocErr> { + unsafe { + use alloc::AllocErr; + + // NOTE: we don't early branch on ZSTs here because we want this + // to actually catch "asking for more than usize::MAX" in that case. + // If we make it past the first branch then we are guaranteed to + // panic. + + // Don't actually need any more capacity. + // Wrapping in case they gave a bad `used_cap`. + if self.cap().wrapping_sub(used_cap) >= needed_extra_cap { + return Ok(()); + } + + // Nothing we can really do about these checks :( + let new_cap = match strategy { + Exact => used_cap.checked_add(needed_extra_cap).ok_or(CapacityOverflow)?, + Amortized => self.amortized_new_size(used_cap, needed_extra_cap)?, + }; + let new_layout = Layout::array::(new_cap).map_err(|_| CapacityOverflow)?; + + alloc_guard(new_layout.size())?; + + let res = match self.current_layout() { + Some(layout) => { + debug_assert!(new_layout.align() == layout.align()); + self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + } + None => self.a.alloc(new_layout), + }; + + match (&res, fallibility) { + (Err(AllocErr), Infallible) => oom(new_layout), + _ => {} + } + + self.ptr = res?.cast().into(); + self.cap = new_cap; + + Ok(()) + } + } + +} + impl RawVec { /// Converts the entire buffer into `Box<[T]>`. /// diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index d0188c6e828..1648fc6b7ef 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -668,7 +668,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom()); + .unwrap_or_else(|_| oom(layout)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 78d3d6d5e60..0c95ceff2e3 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -13,14 +13,14 @@ #![unstable(issue = "32838", feature = "allocator_api")] #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; -#[doc(inline)] pub use alloc_crate::alloc::{Global, oom}; +#[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] -pub extern fn rust_oom() -> ! { +pub extern fn rust_oom(_: Layout) -> ! { rtabort!("memory allocation failed"); } diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index a7eb002d5a1..935ea4b62b5 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,7 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::{CollectionAllocErr, oom}; +use alloc::CollectionAllocErr; use cell::Cell; use borrow::Borrow; use cmp::max; @@ -23,8 +23,10 @@ use mem::{self, replace}; use ops::{Deref, Index}; use sys; -use super::table::{self, Bucket, EmptyBucket, FullBucket, FullBucketMut, RawTable, SafeHash}; +use super::table::{self, Bucket, EmptyBucket, Fallibility, FullBucket, FullBucketMut, RawTable, + SafeHash}; use super::table::BucketState::{Empty, Full}; +use super::table::Fallibility::{Fallible, Infallible}; const MIN_NONZERO_RAW_CAPACITY: usize = 32; // must be a power of two @@ -783,11 +785,11 @@ impl HashMap /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn reserve(&mut self, additional: usize) { - match self.try_reserve(additional) { + match self.reserve_internal(additional, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(()) => { /* yay */ } - } + } } /// Tries to reserve capacity for at least `additional` more elements to be inserted @@ -809,17 +811,24 @@ impl HashMap /// ``` #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.reserve_internal(additional, Fallible) + } + + fn reserve_internal(&mut self, additional: usize, fallibility: Fallibility) + -> Result<(), CollectionAllocErr> { + let remaining = self.capacity() - self.len(); // this can't overflow if remaining < additional { - let min_cap = self.len().checked_add(additional) + let min_cap = self.len() + .checked_add(additional) .ok_or(CollectionAllocErr::CapacityOverflow)?; let raw_cap = self.resize_policy.try_raw_capacity(min_cap)?; - self.try_resize(raw_cap)?; + self.try_resize(raw_cap, fallibility)?; } else if self.table.tag() && remaining <= self.len() { // Probe sequence is too long and table is half full, // resize early to reduce probing length. let new_capacity = self.table.capacity() * 2; - self.try_resize(new_capacity)?; + self.try_resize(new_capacity, fallibility)?; } Ok(()) } @@ -831,11 +840,21 @@ impl HashMap /// 2) Ensure `new_raw_cap` is a power of two or zero. #[inline(never)] #[cold] - fn try_resize(&mut self, new_raw_cap: usize) -> Result<(), CollectionAllocErr> { + fn try_resize( + &mut self, + new_raw_cap: usize, + fallibility: Fallibility, + ) -> Result<(), CollectionAllocErr> { assert!(self.table.size() <= new_raw_cap); assert!(new_raw_cap.is_power_of_two() || new_raw_cap == 0); - let mut old_table = replace(&mut self.table, RawTable::try_new(new_raw_cap)?); + let mut old_table = replace( + &mut self.table, + match fallibility { + Infallible => RawTable::new(new_raw_cap), + Fallible => RawTable::try_new(new_raw_cap)?, + } + ); let old_size = old_table.size(); if old_table.size() == 0 { diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index b50652ed6b5..eed2debcaa2 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -711,11 +711,21 @@ fn test_offset_calculation() { assert_eq!(calculate_offsets(6, 12, 4), (8, 20, false)); } +pub(crate) enum Fallibility { + Fallible, + Infallible, +} + +use self::Fallibility::*; + impl RawTable { /// Does not initialize the buckets. The caller should ensure they, /// at the very least, set every hash to EMPTY_BUCKET. /// Returns an error if it cannot allocate or capacity overflows. - unsafe fn try_new_uninitialized(capacity: usize) -> Result, CollectionAllocErr> { + unsafe fn new_uninitialized_internal( + capacity: usize, + fallibility: Fallibility, + ) -> Result, CollectionAllocErr> { if capacity == 0 { return Ok(RawTable { size: 0, @@ -754,8 +764,12 @@ impl RawTable { return Err(CollectionAllocErr::CapacityOverflow); } - let buffer = Global.alloc(Layout::from_size_align(size, alignment) - .map_err(|_| CollectionAllocErr::CapacityOverflow)?)?; + let layout = Layout::from_size_align(size, alignment) + .map_err(|_| CollectionAllocErr::CapacityOverflow)?; + let buffer = Global.alloc(layout).map_err(|e| match fallibility { + Infallible => oom(layout), + Fallible => e, + })?; Ok(RawTable { capacity_mask: capacity.wrapping_sub(1), @@ -768,9 +782,9 @@ impl RawTable { /// Does not initialize the buckets. The caller should ensure they, /// at the very least, set every hash to EMPTY_BUCKET. unsafe fn new_uninitialized(capacity: usize) -> RawTable { - match Self::try_new_uninitialized(capacity) { + match Self::new_uninitialized_internal(capacity, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(table) => { table } } } @@ -794,22 +808,29 @@ impl RawTable { } } - /// Tries to create a new raw table from a given capacity. If it cannot allocate, - /// it returns with AllocErr. - pub fn try_new(capacity: usize) -> Result, CollectionAllocErr> { + fn new_internal( + capacity: usize, + fallibility: Fallibility, + ) -> Result, CollectionAllocErr> { unsafe { - let ret = RawTable::try_new_uninitialized(capacity)?; + let ret = RawTable::new_uninitialized_internal(capacity, fallibility)?; ptr::write_bytes(ret.hashes.ptr(), 0, capacity); Ok(ret) } } + /// Tries to create a new raw table from a given capacity. If it cannot allocate, + /// it returns with AllocErr. + pub fn try_new(capacity: usize) -> Result, CollectionAllocErr> { + Self::new_internal(capacity, Fallible) + } + /// Creates a new raw table from a given capacity. All buckets are /// initially empty. pub fn new(capacity: usize) -> RawTable { - match Self::try_new(capacity) { + match Self::new_internal(capacity, Infallible) { Err(CollectionAllocErr::CapacityOverflow) => panic!("capacity overflow"), - Err(CollectionAllocErr::AllocErr) => oom(), + Err(CollectionAllocErr::AllocErr) => unreachable!(), Ok(table) => { table } } } diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index 12b115d0938..f1fdbfc702d 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,11 +10,11 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global, oom}; +use std::alloc::{Alloc, Global, Layout, oom}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| oom()); + let ptr = Global.alloc_one::().unwrap_or_else(|_| oom(Layout::new::())); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 308792e5d89..febd249d776 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout.clone()).unwrap_or_else(|_| oom()); + let ret = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -72,8 +72,8 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old.clone(), new.size()) - .unwrap_or_else(|_| oom()); + let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old, new.size()) + .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs index 60a7f70931d..745a19dec4d 100644 --- a/src/test/run-pass/regions-mock-codegen.rs +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -32,8 +32,8 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { - let ptr = Global.alloc(Layout::new::()) - .unwrap_or_else(|_| oom()); + let layout = Layout::new::(); + let ptr = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From 37f5cf563c2c039503e8e50e252f2c1b31d69268 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 17 May 2018 08:17:35 -0700 Subject: Implement `downcast` for `Arc` We only need to implement it for `Any + Send + Sync` because in practice that's the only useful combination for `Arc` and `Any`. Implementation for #44608 under the `rc_downcast` feature. --- src/liballoc/arc.rs | 64 +++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index f7513248784..0795498f87f 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -16,6 +16,7 @@ //! //! [arc]: struct.Arc.html +use core::any::Any; use core::sync::atomic; use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; use core::borrow; @@ -971,6 +972,49 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { } } +impl Arc { + #[inline] + #[unstable(feature = "rc_downcast", issue = "44608")] + /// Attempt to downcast the `Arc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// #![feature(rc_downcast)] + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// fn print_if_string(value: Arc) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// fn main() { + /// let my_string = "Hello World".to_string(); + /// print_if_string(Arc::new(my_string)); + /// print_if_string(Arc::new(0i8)); + /// } + /// ``` + pub fn downcast(self) -> Result, Self> + where + T: Any + Send + Sync + 'static, + { + if (*self).is::() { + unsafe { + let raw: *const ArcInner = self.ptr.as_ptr(); + mem::forget(self); + Ok(Arc { + ptr: NonNull::new_unchecked(raw as *const ArcInner as *mut _), + phantom: PhantomData, + }) + } + } else { + Err(self) + } + } +} + impl Weak { /// Constructs a new `Weak`, allocating memory for `T` without initializing /// it. Calling [`upgrade`] on the return value always gives [`None`]. @@ -1844,6 +1888,26 @@ mod tests { assert_eq!(&r[..], [1, 2, 3]); } + + #[test] + fn test_downcast() { + use std::any::Any; + + let r1: Arc = Arc::new(i32::max_value()); + let r2: Arc = Arc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); + + assert!(r2.clone().downcast::().is_err()); + + let r2str = r2.downcast::<&'static str>(); + assert!(r2str.is_ok()); + assert_eq!(r2str.unwrap(), Arc::new("abc")); + } } #[stable(feature = "rust1", since = "1.0.0")] -- cgit 1.4.1-3-g733a5 From 0c7bf56d805d981bb003a20a4c6b1f6b29790881 Mon Sep 17 00:00:00 2001 From: Jeremy Fitzhardinge Date: Thu, 17 May 2018 08:25:48 -0700 Subject: Update `Arc` and `Rc` to use `NonNull::cast` to cast the inner pointers This avoids an `unsafe` block in each case. --- src/liballoc/arc.rs | 11 +++-------- src/liballoc/rc.rs | 12 +++--------- 2 files changed, 6 insertions(+), 17 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 0795498f87f..4026b3ababa 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -1001,14 +1001,9 @@ impl Arc { T: Any + Send + Sync + 'static, { if (*self).is::() { - unsafe { - let raw: *const ArcInner = self.ptr.as_ptr(); - mem::forget(self); - Ok(Arc { - ptr: NonNull::new_unchecked(raw as *const ArcInner as *mut _), - phantom: PhantomData, - }) - } + let ptr = self.ptr.cast::>(); + mem::forget(self); + Ok(Arc { ptr, phantom: PhantomData }) } else { Err(self) } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 1648fc6b7ef..553c8b5ca32 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -644,15 +644,9 @@ impl Rc { /// ``` pub fn downcast(self) -> Result, Rc> { if (*self).is::() { - // avoid the pointer arithmetic in from_raw - unsafe { - let raw: *const RcBox = self.ptr.as_ptr(); - forget(self); - Ok(Rc { - ptr: NonNull::new_unchecked(raw as *const RcBox as *mut _), - phantom: PhantomData, - }) - } + let ptr = self.ptr.cast::>(); + forget(self); + Ok(Rc { ptr, phantom: PhantomData }) } else { Err(self) } -- cgit 1.4.1-3-g733a5 From 9d770e9959ed5fedad31bfc04f946f5e268cfc37 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Mon, 28 May 2018 20:19:39 -0600 Subject: Stabilize SliceIndex trait. Fixes #35729 According to recommendations in https://github.com/rust-lang/rust/issues/35729#issuecomment-377784884 --- src/liballoc/lib.rs | 1 - src/liballoc/slice.rs | 2 +- src/libcore/slice/mod.rs | 32 ++++++++++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 91de3ad0c39..a56420d52d0 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -104,7 +104,6 @@ #![feature(ptr_internals)] #![feature(ptr_offset_from)] #![feature(rustc_attrs)] -#![feature(slice_get_slice)] #![feature(specialization)] #![feature(staged_api)] #![feature(str_internals)] diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 161493f3892..8686ecd7bcf 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -121,7 +121,7 @@ pub use core::slice::{RSplit, RSplitMut}; pub use core::slice::{from_raw_parts, from_raw_parts_mut}; #[stable(feature = "from_ref", since = "1.28.0")] pub use core::slice::{from_ref, from_mut}; -#[unstable(feature = "slice_get_slice", issue = "35729")] +#[stable(feature = "slice_get_slice", since = "1.28.0")] pub use core::slice::SliceIndex; #[unstable(feature = "exact_chunks", issue = "47115")] pub use core::slice::{ExactChunks, ExactChunksMut}; diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index d52cc8cbe3f..43236c33104 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1977,35 +1977,63 @@ fn slice_index_overflow_fail() -> ! { panic!("attempted to index slice up to maximum usize"); } +mod private_slice_index { + use super::ops; + #[stable(feature = "slice_get_slice", since = "1.28.0")] + pub trait Sealed {} + + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for usize {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::Range {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeTo {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeFrom {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeFull {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeInclusive {} + #[stable(feature = "slice_get_slice", since = "1.28.0")] + impl Sealed for ops::RangeToInclusive {} +} + /// A helper trait used for indexing operations. -#[unstable(feature = "slice_get_slice", issue = "35729")] +#[stable(feature = "slice_get_slice", since = "1.28.0")] #[rustc_on_unimplemented = "slice indices are of type `usize` or ranges of `usize`"] -pub trait SliceIndex { +pub trait SliceIndex: private_slice_index::Sealed { /// The output type returned by methods. + #[stable(feature = "slice_get_slice", since = "1.28.0")] type Output: ?Sized; /// Returns a shared reference to the output at this location, if in /// bounds. + #[unstable(feature = "slice_index_methods", issue = "0")] fn get(self, slice: &T) -> Option<&Self::Output>; /// Returns a mutable reference to the output at this location, if in /// bounds. + #[unstable(feature = "slice_index_methods", issue = "0")] fn get_mut(self, slice: &mut T) -> Option<&mut Self::Output>; /// Returns a shared reference to the output at this location, without /// performing any bounds checking. + #[unstable(feature = "slice_index_methods", issue = "0")] unsafe fn get_unchecked(self, slice: &T) -> &Self::Output; /// Returns a mutable reference to the output at this location, without /// performing any bounds checking. + #[unstable(feature = "slice_index_methods", issue = "0")] unsafe fn get_unchecked_mut(self, slice: &mut T) -> &mut Self::Output; /// Returns a shared reference to the output at this location, panicking /// if out of bounds. + #[unstable(feature = "slice_index_methods", issue = "0")] fn index(self, slice: &T) -> &Self::Output; /// Returns a mutable reference to the output at this location, panicking /// if out of bounds. + #[unstable(feature = "slice_index_methods", issue = "0")] fn index_mut(self, slice: &mut T) -> &mut Self::Output; } -- cgit 1.4.1-3-g733a5 From d86608205069aed5c78bcc38dd26bcf4213e23a0 Mon Sep 17 00:00:00 2001 From: Emerentius Date: Mon, 30 Apr 2018 13:09:10 +0200 Subject: optimize joining and concatenation for slices for both Vec and String - eliminates the boolean first flag in fn join() for String only - eliminates repeated bounds checks in join(), concat() - adds fast paths for small string separators up to a len of 4 bytes --- src/liballoc/slice.rs | 22 ++++---- src/liballoc/str.rs | 138 +++++++++++++++++++++++++++++++++++++------------- 2 files changed, 113 insertions(+), 47 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 161493f3892..82578c3206f 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -566,18 +566,18 @@ impl> SliceConcatExt for [V] { } fn join(&self, sep: &T) -> Vec { - let size = self.iter().fold(0, |acc, v| acc + v.borrow().len()); - let mut result = Vec::with_capacity(size + self.len()); - let mut first = true; - for v in self { - if first { - first = false - } else { - result.push(sep.clone()) + let mut iter = self.iter(); + iter.next().map_or(vec![], |first| { + let size = self.iter().fold(0, |acc, v| acc + v.borrow().len()); + let mut result = Vec::with_capacity(size + self.len()); + result.extend_from_slice(first.borrow()); + + for v in iter { + result.push(sep.clone()); + result.extend_from_slice(v.borrow()) } - result.extend_from_slice(v.borrow()) - } - result + result + }) } fn connect(&self, sep: &T) -> Vec { diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 823e56b64e3..45daabf86ab 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -86,52 +86,118 @@ impl> SliceConcatExt for [S] { type Output = String; fn concat(&self) -> String { - if self.is_empty() { - return String::new(); - } - - // `len` calculation may overflow but push_str will check boundaries - let len = self.iter().map(|s| s.borrow().len()).sum(); - let mut result = String::with_capacity(len); - - for s in self { - result.push_str(s.borrow()) - } - - result + self.join("") } fn join(&self, sep: &str) -> String { - if self.is_empty() { - return String::new(); + unsafe { + String::from_utf8_unchecked( join_generic_copy(self, sep.as_bytes()) ) } + } - // concat is faster - if sep.is_empty() { - return self.concat(); - } + fn connect(&self, sep: &str) -> String { + self.join(sep) + } +} - // this is wrong without the guarantee that `self` is non-empty - // `len` calculation may overflow but push_str but will check boundaries - let len = sep.len() * (self.len() - 1) + - self.iter().map(|s| s.borrow().len()).sum::(); - let mut result = String::with_capacity(len); - let mut first = true; +macro_rules! spezialize_for_lengths { + ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => { + let mut target = $target; + let iter = $iter; + let sep_len = $separator.len(); + let sep_bytes = $separator; + match $separator.len() { + $( + // loops with hardcoded sizes run much faster + // specialize the cases with small separator lengths + $num => { + for s in iter { + target.get_unchecked_mut(..$num) + .copy_from_slice(sep_bytes); + + let s_bytes = s.borrow().as_ref(); + let offset = s_bytes.len(); + target = {target}.get_unchecked_mut($num..); + target.get_unchecked_mut(..offset) + .copy_from_slice(s_bytes); + target = {target}.get_unchecked_mut(offset..); + } + }, + )* + 0 => { + // concat, same principle without the separator + for s in iter { + let s_bytes = s.borrow().as_ref(); + let offset = s_bytes.len(); + target.get_unchecked_mut(..offset) + .copy_from_slice(s_bytes); + target = {target}.get_unchecked_mut(offset..); + } + }, + _ => { + // arbitrary non-zero size fallback + for s in iter { + target.get_unchecked_mut(..sep_len) + .copy_from_slice(sep_bytes); + + let s_bytes = s.borrow().as_ref(); + let offset = s_bytes.len(); + target = {target}.get_unchecked_mut(sep_len..); + target.get_unchecked_mut(..offset) + .copy_from_slice(s_bytes); + target = {target}.get_unchecked_mut(offset..); + } + } + } + }; +} - for s in self { - if first { - first = false; - } else { - result.push_str(sep); +// Optimized join implementation that works for both Vec (T: Copy) and String's inner vec +// Currently (2018-05-13) there is a bug with type inference and specialization (see issue #36262) +// For this reason SliceConcatExt is not specialized for T: Copy and SliceConcatExt is the +// only user of this function. It is left in place for the time when that is fixed. +// +// the bounds for String-join are S: Borrow and for Vec-join Borrow<[T]> +// [T] and str both impl AsRef<[T]> for some T +// => s.borrow().as_ref() and we always have slices +fn join_generic_copy(slice: &[S], sep: &[T]) -> Vec +where + T: Copy, + B: AsRef<[T]> + ?Sized, + S: Borrow, +{ + let sep_len = sep.len(); + let mut iter = slice.iter(); + iter.next().map_or(vec![], |first| { + // this is wrong without the guarantee that `slice` is non-empty + // if the `len` calculation overflows, we'll panic + // we would have run out of memory anyway and the rest of the function requires + // the entire String pre-allocated for safety + // + // this is the exact len of the resulting String + let len = sep_len.checked_mul(slice.len() - 1).and_then(|n| { + slice.iter().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add) + }).expect("attempt to join into collection with len > usize::MAX"); + + // crucial for safety + let mut result = Vec::with_capacity(len); + + unsafe { + result.extend_from_slice(first.borrow().as_ref()); + + { + let pos = result.len(); + let target = result.get_unchecked_mut(pos..len); + + // copy separator and strs over without bounds checks + // generate loops with hardcoded offsets for small separators + // massive improvements possible (~ x2) + spezialize_for_lengths!(sep, target, iter; 1, 2, 3, 4); } - result.push_str(s.borrow()); + result.set_len(len); } result - } - - fn connect(&self, sep: &str) -> String { - self.join(sep) - } + }) } #[stable(feature = "rust1", since = "1.0.0")] -- cgit 1.4.1-3-g733a5 From b2fd7da0cf0b835a540d333b4b72426b4735c586 Mon Sep 17 00:00:00 2001 From: Emerentius Date: Mon, 7 May 2018 17:37:13 +0200 Subject: add more join tests old tests cover the new fast path of str joining already this adds tests for joining into Strings with long separators (>4 byte) and for joining into Vec, T: Clone + !Copy. Vec will be specialised when specialisation type inference bugs are fixed. --- src/liballoc/tests/slice.rs | 9 +++++++++ src/liballoc/tests/str.rs | 13 +++++++++++++ 2 files changed, 22 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/slice.rs b/src/liballoc/tests/slice.rs index 6fd0b33f02a..3b7eec38609 100644 --- a/src/liballoc/tests/slice.rs +++ b/src/liballoc/tests/slice.rs @@ -609,6 +609,15 @@ fn test_join() { assert_eq!(v.join(&0), [1, 0, 2, 0, 3]); } +#[test] +fn test_join_nocopy() { + let v: [String; 0] = []; + assert_eq!(v.join(","), ""); + assert_eq!(["a".to_string(), "ab".into()].join(","), "a,ab"); + assert_eq!(["a".to_string(), "ab".into(), "abc".into()].join(","), "a,ab,abc"); + assert_eq!(["a".to_string(), "ab".into(), "".into()].join(","), "a,ab,"); +} + #[test] fn test_insert() { let mut a = vec![1, 2, 4]; diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index d11bf5dc3e9..03d295d16e6 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -162,6 +162,19 @@ fn test_join_for_different_lengths() { test_join!("-a-bc", ["", "a", "bc"], "-"); } +// join has fast paths for small separators up to 4 bytes +// this tests the slow paths. +#[test] +fn test_join_for_different_lengths_with_long_separator() { + assert_eq!("~~~~~".len(), 15); + + let empty: &[&str] = &[]; + test_join!("", empty, "~~~~~"); + test_join!("a", ["a"], "~~~~~"); + test_join!("a~~~~~b", ["a", "b"], "~~~~~"); + test_join!("~~~~~a~~~~~bc", ["", "a", "bc"], "~~~~~"); +} + #[test] fn test_unsafe_slice() { assert_eq!("ab", unsafe {"abc".slice_unchecked(0, 2)}); -- cgit 1.4.1-3-g733a5 From d0d0885c3fa85fd05946d69599a3ddd886c9671f Mon Sep 17 00:00:00 2001 From: Emerentius Date: Wed, 23 May 2018 07:20:37 +0200 Subject: compacts join code --- src/liballoc/str.rs | 44 ++++++++++++++------------------------------ 1 file changed, 14 insertions(+), 30 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 45daabf86ab..4db6d5d715c 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -104,7 +104,6 @@ macro_rules! spezialize_for_lengths { ($separator:expr, $target:expr, $iter:expr; $($num:expr),*) => { let mut target = $target; let iter = $iter; - let sep_len = $separator.len(); let sep_bytes = $separator; match $separator.len() { $( @@ -112,46 +111,31 @@ macro_rules! spezialize_for_lengths { // specialize the cases with small separator lengths $num => { for s in iter { - target.get_unchecked_mut(..$num) - .copy_from_slice(sep_bytes); - - let s_bytes = s.borrow().as_ref(); - let offset = s_bytes.len(); - target = {target}.get_unchecked_mut($num..); - target.get_unchecked_mut(..offset) - .copy_from_slice(s_bytes); - target = {target}.get_unchecked_mut(offset..); + copy_slice_and_advance!(target, sep_bytes); + copy_slice_and_advance!(target, s.borrow().as_ref()); } }, )* - 0 => { - // concat, same principle without the separator - for s in iter { - let s_bytes = s.borrow().as_ref(); - let offset = s_bytes.len(); - target.get_unchecked_mut(..offset) - .copy_from_slice(s_bytes); - target = {target}.get_unchecked_mut(offset..); - } - }, _ => { // arbitrary non-zero size fallback for s in iter { - target.get_unchecked_mut(..sep_len) - .copy_from_slice(sep_bytes); - - let s_bytes = s.borrow().as_ref(); - let offset = s_bytes.len(); - target = {target}.get_unchecked_mut(sep_len..); - target.get_unchecked_mut(..offset) - .copy_from_slice(s_bytes); - target = {target}.get_unchecked_mut(offset..); + copy_slice_and_advance!(target, sep_bytes); + copy_slice_and_advance!(target, s.borrow().as_ref()); } } } }; } +macro_rules! copy_slice_and_advance { + ($target:expr, $bytes:expr) => { + let len = $bytes.len(); + $target.get_unchecked_mut(..len) + .copy_from_slice($bytes); + $target = {$target}.get_unchecked_mut(len..); + } +} + // Optimized join implementation that works for both Vec (T: Copy) and String's inner vec // Currently (2018-05-13) there is a bug with type inference and specialization (see issue #36262) // For this reason SliceConcatExt is not specialized for T: Copy and SliceConcatExt is the @@ -192,7 +176,7 @@ where // copy separator and strs over without bounds checks // generate loops with hardcoded offsets for small separators // massive improvements possible (~ x2) - spezialize_for_lengths!(sep, target, iter; 1, 2, 3, 4); + spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); } result.set_len(len); } -- cgit 1.4.1-3-g733a5 From 12bd28874697d600d347518c8636053b92e81801 Mon Sep 17 00:00:00 2001 From: Emerentius Date: Fri, 25 May 2018 23:53:22 +0200 Subject: incorporate changes from code review further reduce unsafe fn calls reduce right drift assert! sufficient capacity --- src/liballoc/slice.rs | 24 +++++++++++---------- src/liballoc/str.rs | 60 ++++++++++++++++++++++++++++----------------------- 2 files changed, 46 insertions(+), 38 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/slice.rs b/src/liballoc/slice.rs index 82578c3206f..90bc2f9769c 100644 --- a/src/liballoc/slice.rs +++ b/src/liballoc/slice.rs @@ -567,17 +567,19 @@ impl> SliceConcatExt for [V] { fn join(&self, sep: &T) -> Vec { let mut iter = self.iter(); - iter.next().map_or(vec![], |first| { - let size = self.iter().fold(0, |acc, v| acc + v.borrow().len()); - let mut result = Vec::with_capacity(size + self.len()); - result.extend_from_slice(first.borrow()); - - for v in iter { - result.push(sep.clone()); - result.extend_from_slice(v.borrow()) - } - result - }) + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; + let size = self.iter().fold(0, |acc, v| acc + v.borrow().len()); + let mut result = Vec::with_capacity(size + self.len()); + result.extend_from_slice(first.borrow()); + + for v in iter { + result.push(sep.clone()); + result.extend_from_slice(v.borrow()) + } + result } fn connect(&self, sep: &T) -> Vec { diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 4db6d5d715c..32ca8d1fa5e 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -130,9 +130,9 @@ macro_rules! spezialize_for_lengths { macro_rules! copy_slice_and_advance { ($target:expr, $bytes:expr) => { let len = $bytes.len(); - $target.get_unchecked_mut(..len) - .copy_from_slice($bytes); - $target = {$target}.get_unchecked_mut(len..); + let (head, tail) = {$target}.split_at_mut(len); + head.copy_from_slice($bytes); + $target = tail; } } @@ -152,36 +152,42 @@ where { let sep_len = sep.len(); let mut iter = slice.iter(); - iter.next().map_or(vec![], |first| { - // this is wrong without the guarantee that `slice` is non-empty - // if the `len` calculation overflows, we'll panic - // we would have run out of memory anyway and the rest of the function requires - // the entire String pre-allocated for safety - // - // this is the exact len of the resulting String - let len = sep_len.checked_mul(slice.len() - 1).and_then(|n| { - slice.iter().map(|s| s.borrow().as_ref().len()).try_fold(n, usize::checked_add) + + // the first slice is the only one without a separator preceding it + let first = match iter.next() { + Some(first) => first, + None => return vec![], + }; + + // compute the exact total length of the joined Vec + // if the `len` calculation overflows, we'll panic + // we would have run out of memory anyway and the rest of the function requires + // the entire Vec pre-allocated for safety + let len = sep_len.checked_mul(iter.len()).and_then(|n| { + slice.iter() + .map(|s| s.borrow().as_ref().len()) + .try_fold(n, usize::checked_add) }).expect("attempt to join into collection with len > usize::MAX"); - // crucial for safety - let mut result = Vec::with_capacity(len); + // crucial for safety + let mut result = Vec::with_capacity(len); + assert!(result.capacity() >= len); - unsafe { - result.extend_from_slice(first.borrow().as_ref()); + result.extend_from_slice(first.borrow().as_ref()); - { - let pos = result.len(); - let target = result.get_unchecked_mut(pos..len); + unsafe { + { + let pos = result.len(); + let target = result.get_unchecked_mut(pos..len); - // copy separator and strs over without bounds checks - // generate loops with hardcoded offsets for small separators - // massive improvements possible (~ x2) - spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); - } - result.set_len(len); + // copy separator and slices over without bounds checks + // generate loops with hardcoded offsets for small separators + // massive improvements possible (~ x2) + spezialize_for_lengths!(sep, target, iter; 0, 1, 2, 3, 4); } - result - }) + result.set_len(len); + } + result } #[stable(feature = "rust1", since = "1.0.0")] -- cgit 1.4.1-3-g733a5 From 0ff8d40fa1c6be4428a65f6539ffe98c83245eab Mon Sep 17 00:00:00 2001 From: kennytm Date: Sun, 3 Jun 2018 00:29:50 +0800 Subject: impl Default for &mut str --- src/liballoc/tests/str.rs | 1 + src/libcore/str/mod.rs | 6 ++++++ 2 files changed, 7 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 03d295d16e6..75306ac82df 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -1326,6 +1326,7 @@ fn test_str_default() { t::<&str>(); t::(); + t::<&mut str>(); } #[test] diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 3169893fcde..5e1a9c25a21 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -3875,6 +3875,12 @@ impl<'a> Default for &'a str { fn default() -> &'a str { "" } } +#[stable(feature = "default_mut_str", since = "1.28.0")] +impl<'a> Default for &'a mut str { + /// Creates an empty mutable str + fn default() -> &'a mut str { unsafe { from_utf8_unchecked_mut(&mut []) } } +} + /// An iterator over the non-whitespace substrings of a string, /// separated by any amount of whitespace. /// -- cgit 1.4.1-3-g733a5 From dd88f88c02c901dd14e18a65c0b7132c36f530ee Mon Sep 17 00:00:00 2001 From: Corey Farwell Date: Sat, 2 Jun 2018 16:06:17 -0400 Subject: Copy changes from HashMap over to BTreeMap. --- src/liballoc/btree/map.rs | 17 +++++++++++++++-- 1 file changed, 15 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 28c42144b2a..9b6f91c039f 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2369,6 +2369,11 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { /// Gets a mutable reference to the value in the entry. /// + /// If you need a reference to the `OccupiedEntry` which may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: #method.into_mut + /// /// # Examples /// /// ``` @@ -2380,9 +2385,13 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { /// /// assert_eq!(map["poneyland"], 12); /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; /// } - /// assert_eq!(map["poneyland"], 22); + /// assert_eq!(map["poneyland"], 24); /// ``` #[stable(feature = "rust1", since = "1.0.0")] pub fn get_mut(&mut self) -> &mut V { @@ -2391,6 +2400,10 @@ impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { /// Converts the entry into a mutable reference to its value. /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: #method.get_mut + /// /// # Examples /// /// ``` -- cgit 1.4.1-3-g733a5 From 72e17b81fa88894e3fe04f221166f5a48e753e94 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Sat, 2 Jun 2018 20:42:42 -0600 Subject: Stabilize Iterator::step_by Fixes #27741 --- src/liballoc/tests/lib.rs | 1 - src/libcore/iter/iterator.rs | 5 +---- src/libcore/iter/mod.rs | 12 +++--------- src/libcore/tests/lib.rs | 1 - src/test/run-pass/range_inclusive.rs | 2 -- src/test/run-pass/sync-send-iterators-in-libcore.rs | 1 - 6 files changed, 4 insertions(+), 18 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 081c473768f..dbac2c0bb18 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -15,7 +15,6 @@ #![feature(const_fn)] #![feature(drain_filter)] #![feature(exact_size_is_empty)] -#![feature(iterator_step_by)] #![feature(pattern)] #![feature(rand)] #![feature(slice_sort_by_cached_key)] diff --git a/src/libcore/iter/iterator.rs b/src/libcore/iter/iterator.rs index f5b23a3793b..1972b009905 100644 --- a/src/libcore/iter/iterator.rs +++ b/src/libcore/iter/iterator.rs @@ -283,7 +283,6 @@ pub trait Iterator { /// Basic usage: /// /// ``` - /// #![feature(iterator_step_by)] /// let a = [0, 1, 2, 3, 4, 5]; /// let mut iter = a.into_iter().step_by(2); /// @@ -293,9 +292,7 @@ pub trait Iterator { /// assert_eq!(iter.next(), None); /// ``` #[inline] - #[unstable(feature = "iterator_step_by", - reason = "unstable replacement of Range::step_by", - issue = "27741")] + #[stable(feature = "iterator_step_by", since = "1.28.0")] fn step_by(self, step: usize) -> StepBy where Self: Sized { assert!(step != 0); StepBy{iter: self, step: step - 1, first_take: true} diff --git a/src/libcore/iter/mod.rs b/src/libcore/iter/mod.rs index 1e8476d3880..3458527c322 100644 --- a/src/libcore/iter/mod.rs +++ b/src/libcore/iter/mod.rs @@ -673,9 +673,7 @@ impl FusedIterator for Cycle where I: Clone + Iterator {} /// [`step_by`]: trait.Iterator.html#method.step_by /// [`Iterator`]: trait.Iterator.html #[must_use = "iterator adaptors are lazy and do nothing unless consumed"] -#[unstable(feature = "iterator_step_by", - reason = "unstable replacement of Range::step_by", - issue = "27741")] +#[stable(feature = "iterator_step_by", since = "1.28.0")] #[derive(Clone, Debug)] pub struct StepBy { iter: I, @@ -683,9 +681,7 @@ pub struct StepBy { first_take: bool, } -#[unstable(feature = "iterator_step_by", - reason = "unstable replacement of Range::step_by", - issue = "27741")] +#[stable(feature = "iterator_step_by", since = "1.28.0")] impl Iterator for StepBy where I: Iterator { type Item = I::Item; @@ -757,9 +753,7 @@ impl Iterator for StepBy where I: Iterator { } // StepBy can only make the iterator shorter, so the len will still fit. -#[unstable(feature = "iterator_step_by", - reason = "unstable replacement of Range::step_by", - issue = "27741")] +#[stable(feature = "iterator_step_by", since = "1.28.0")] impl ExactSizeIterator for StepBy where I: ExactSizeIterator {} /// An iterator that strings two iterators together. diff --git a/src/libcore/tests/lib.rs b/src/libcore/tests/lib.rs index 13189d532ab..7c62d0d758d 100644 --- a/src/libcore/tests/lib.rs +++ b/src/libcore/tests/lib.rs @@ -23,7 +23,6 @@ #![feature(flt2dec)] #![feature(fmt_internals)] #![feature(hashmap_internals)] -#![feature(iterator_step_by)] #![feature(iterator_flatten)] #![feature(iterator_repeat_with)] #![feature(pattern)] diff --git a/src/test/run-pass/range_inclusive.rs b/src/test/run-pass/range_inclusive.rs index 5d46bfab887..b08d16e5088 100644 --- a/src/test/run-pass/range_inclusive.rs +++ b/src/test/run-pass/range_inclusive.rs @@ -10,8 +10,6 @@ // Test inclusive range syntax. -#![feature(iterator_step_by)] - use std::ops::{RangeInclusive, RangeToInclusive}; fn foo() -> isize { 42 } diff --git a/src/test/run-pass/sync-send-iterators-in-libcore.rs b/src/test/run-pass/sync-send-iterators-in-libcore.rs index c11a0d391a4..bb190543ecd 100644 --- a/src/test/run-pass/sync-send-iterators-in-libcore.rs +++ b/src/test/run-pass/sync-send-iterators-in-libcore.rs @@ -14,7 +14,6 @@ #![feature(iter_empty)] #![feature(iter_once)] #![feature(iter_unfold)] -#![feature(iterator_step_by)] #![feature(str_escape)] use std::iter::{empty, once, repeat}; -- cgit 1.4.1-3-g733a5 From 804984836eb98fd61bc5f03aff8756a9c1cf2fa4 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sun, 3 Jun 2018 17:04:48 +0200 Subject: Reexport fmt::Alignment into std --- src/liballoc/fmt.rs | 2 ++ src/libcore/fmt/mod.rs | 3 +-- 2 files changed, 3 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/fmt.rs b/src/liballoc/fmt.rs index a4e5373d907..b49ec0ae252 100644 --- a/src/liballoc/fmt.rs +++ b/src/liballoc/fmt.rs @@ -531,6 +531,8 @@ pub use core::fmt::Error; pub use core::fmt::{write, ArgumentV1, Arguments}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::fmt::{DebugList, DebugMap, DebugSet, DebugStruct, DebugTuple}; +#[stable(feature = "fmt_flags_align", since = "1.28.0")] +pub use core::fmt::{Alignment}; use string; diff --git a/src/libcore/fmt/mod.rs b/src/libcore/fmt/mod.rs index 0515eeed30b..d91bf463383 100644 --- a/src/libcore/fmt/mod.rs +++ b/src/libcore/fmt/mod.rs @@ -1436,8 +1436,7 @@ impl<'a> Formatter<'a> { /// ``` /// extern crate core; /// - /// use std::fmt; - /// use core::fmt::Alignment; + /// use std::fmt::{self, Alignment}; /// /// struct Foo; /// -- cgit 1.4.1-3-g733a5 From a6055c885917093faf37bcb834350df7b6ddca82 Mon Sep 17 00:00:00 2001 From: Taylor Cramer Date: Wed, 30 May 2018 18:23:10 -0700 Subject: Add Future and task system to the standard library --- src/liballoc/boxed.rs | 93 +++++++ src/liballoc/lib.rs | 6 + src/liballoc/task.rs | 140 +++++++++++ src/libcore/future.rs | 93 +++++++ src/libcore/lib.rs | 5 + src/libcore/task.rs | 513 +++++++++++++++++++++++++++++++++++++++ src/libstd/lib.rs | 16 ++ src/test/run-pass/futures-api.rs | 95 ++++++++ 8 files changed, 961 insertions(+) create mode 100644 src/liballoc/task.rs create mode 100644 src/libcore/future.rs create mode 100644 src/libcore/task.rs create mode 100644 src/test/run-pass/futures-api.rs (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index a83ce7f379f..a64b94b6517 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -59,12 +59,14 @@ use core::any::Any; use core::borrow; use core::cmp::Ordering; use core::fmt; +use core::future::Future; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; +use core::task::{Context, Poll, UnsafePoll, TaskObj}; use core::convert::From; use raw_vec::RawVec; @@ -755,6 +757,7 @@ impl Generator for Box /// A pinned, heap allocated reference. #[unstable(feature = "pin", issue = "49150")] #[fundamental] +#[repr(transparent)] pub struct PinBox { inner: Box, } @@ -771,14 +774,72 @@ impl PinBox { #[unstable(feature = "pin", issue = "49150")] impl PinBox { /// Get a pinned reference to the data in this PinBox. + #[inline] pub fn as_pin_mut<'a>(&'a mut self) -> PinMut<'a, T> { unsafe { PinMut::new_unchecked(&mut *self.inner) } } + /// Constructs a `PinBox` from a raw pointer. + /// + /// After calling this function, the raw pointer is owned by the + /// resulting `PinBox`. Specifically, the `PinBox` destructor will call + /// the destructor of `T` and free the allocated memory. Since the + /// way `PinBox` allocates and releases memory is unspecified, the + /// only valid pointer to pass to this function is the one taken + /// from another `PinBox` via the [`PinBox::into_raw`] function. + /// + /// This function is unsafe because improper use may lead to + /// memory problems. For example, a double-free may occur if the + /// function is called twice on the same raw pointer. + /// + /// [`PinBox::into_raw`]: struct.PinBox.html#method.into_raw + /// + /// # Examples + /// + /// ``` + /// #![feature(pin)] + /// use std::boxed::PinBox; + /// let x = PinBox::new(5); + /// let ptr = PinBox::into_raw(x); + /// let x = unsafe { PinBox::from_raw(ptr) }; + /// ``` + #[inline] + pub unsafe fn from_raw(raw: *mut T) -> Self { + PinBox { inner: Box::from_raw(raw) } + } + + /// Consumes the `PinBox`, returning the wrapped raw pointer. + /// + /// After calling this function, the caller is responsible for the + /// memory previously managed by the `PinBox`. In particular, the + /// caller should properly destroy `T` and release the memory. The + /// proper way to do so is to convert the raw pointer back into a + /// `PinBox` with the [`PinBox::from_raw`] function. + /// + /// Note: this is an associated function, which means that you have + /// to call it as `PinBox::into_raw(b)` instead of `b.into_raw()`. This + /// is so that there is no conflict with a method on the inner type. + /// + /// [`PinBox::from_raw`]: struct.PinBox.html#method.from_raw + /// + /// # Examples + /// + /// ``` + /// #![feature(pin)] + /// use std::boxed::PinBox; + /// let x = PinBox::new(5); + /// let ptr = PinBox::into_raw(x); + /// ``` + #[inline] + pub fn into_raw(b: PinBox) -> *mut T { + Box::into_raw(b.inner) + } + /// Get a mutable reference to the data inside this PinBox. /// /// This function is unsafe. Users must guarantee that the data is never /// moved out of this reference. + #[inline] pub unsafe fn get_mut<'a>(this: &'a mut PinBox) -> &'a mut T { &mut *this.inner } @@ -787,6 +848,7 @@ impl PinBox { /// /// This function is unsafe. Users must guarantee that the data is never /// moved out of the box. + #[inline] pub unsafe fn unpin(this: PinBox) -> Box { this.inner } @@ -851,3 +913,34 @@ impl, U: ?Sized> CoerceUnsized> for PinBox {} #[unstable(feature = "pin", issue = "49150")] impl Unpin for PinBox {} + +#[unstable(feature = "futures_api", issue = "50547")] +unsafe impl + Send + 'static> UnsafePoll for PinBox { + fn into_raw(self) -> *mut () { + PinBox::into_raw(self) as *mut () + } + + unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()> { + let ptr = task as *mut F; + let pin: PinMut = PinMut::new_unchecked(&mut *ptr); + pin.poll(cx) + } + + unsafe fn drop(task: *mut ()) { + drop(PinBox::from_raw(task as *mut F)) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +impl + Send + 'static> From> for TaskObj { + fn from(boxed: PinBox) -> Self { + TaskObj::from_poll_task(boxed) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +impl + Send + 'static> From> for TaskObj { + fn from(boxed: Box) -> Self { + TaskObj::from_poll_task(PinBox::from(boxed)) + } +} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 91de3ad0c39..e0729d3a467 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -95,6 +95,7 @@ #![feature(fmt_internals)] #![feature(from_ref)] #![feature(fundamental)] +#![feature(futures_api)] #![feature(lang_items)] #![feature(libc)] #![feature(needs_allocator)] @@ -103,6 +104,7 @@ #![feature(pin)] #![feature(ptr_internals)] #![feature(ptr_offset_from)] +#![feature(repr_transparent)] #![feature(rustc_attrs)] #![feature(slice_get_slice)] #![feature(specialization)] @@ -156,6 +158,10 @@ pub mod heap { pub use alloc::*; } +#[unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] +pub mod task; // Primitive types using the heaps above diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs new file mode 100644 index 00000000000..7b1947b56b8 --- /dev/null +++ b/src/liballoc/task.rs @@ -0,0 +1,140 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Types and Traits for working with asynchronous tasks. + +pub use core::task::*; + +#[cfg(target_has_atomic = "ptr")] +pub use self::if_arc::*; + +#[cfg(target_has_atomic = "ptr")] +mod if_arc { + use super::*; + use arc::Arc; + use core::marker::PhantomData; + use core::mem; + use core::ptr::{self, NonNull}; + + /// A way of waking up a specific task. + /// + /// Any task executor must provide a way of signaling that a task it owns + /// is ready to be `poll`ed again. Executors do so by implementing this trait. + pub trait Wake: Send + Sync { + /// Indicates that the associated task is ready to make progress and should + /// be `poll`ed. + /// + /// Executors generally maintain a queue of "ready" tasks; `wake` should place + /// the associated task onto this queue. + fn wake(arc_self: &Arc); + + /// Indicates that the associated task is ready to make progress and should + /// be `poll`ed. This function is like `wake`, but can only be called from the + /// thread on which this `Wake` was created. + /// + /// Executors generally maintain a queue of "ready" tasks; `wake_local` should place + /// the associated task onto this queue. + #[inline] + unsafe fn wake_local(arc_self: &Arc) { + Self::wake(arc_self); + } + } + + #[cfg(target_has_atomic = "ptr")] + struct ArcWrapped(PhantomData); + + unsafe impl UnsafeWake for ArcWrapped { + #[inline] + unsafe fn clone_raw(&self) -> Waker { + let me: *const ArcWrapped = self; + let arc = (*(&me as *const *const ArcWrapped as *const Arc)).clone(); + Waker::from(arc) + } + + #[inline] + unsafe fn drop_raw(&self) { + let mut me: *const ArcWrapped = self; + let me = &mut me as *mut *const ArcWrapped as *mut Arc; + ptr::drop_in_place(me); + } + + #[inline] + unsafe fn wake(&self) { + let me: *const ArcWrapped = self; + T::wake(&*(&me as *const *const ArcWrapped as *const Arc)) + } + + #[inline] + unsafe fn wake_local(&self) { + let me: *const ArcWrapped = self; + T::wake_local(&*(&me as *const *const ArcWrapped as *const Arc)) + } + } + + impl From> for Waker + where T: Wake + 'static, + { + fn from(rc: Arc) -> Self { + unsafe { + let ptr = mem::transmute::, NonNull>>(rc); + Waker::new(ptr) + } + } + } + + /// Creates a `LocalWaker` from a local `wake`. + /// + /// This function requires that `wake` is "local" (created on the current thread). + /// The resulting `LocalWaker` will call `wake.wake_local()` when awoken, and + /// will call `wake.wake()` if awoken after being converted to a `Waker`. + #[inline] + pub unsafe fn local_waker(wake: Arc) -> LocalWaker { + let ptr = mem::transmute::, NonNull>>(wake); + LocalWaker::new(ptr) + } + + struct NonLocalAsLocal(ArcWrapped); + + unsafe impl UnsafeWake for NonLocalAsLocal { + #[inline] + unsafe fn clone_raw(&self) -> Waker { + self.0.clone_raw() + } + + #[inline] + unsafe fn drop_raw(&self) { + self.0.drop_raw() + } + + #[inline] + unsafe fn wake(&self) { + self.0.wake() + } + + #[inline] + unsafe fn wake_local(&self) { + // Since we're nonlocal, we can't call wake_local + self.0.wake() + } + } + + /// Creates a `LocalWaker` from a non-local `wake`. + /// + /// This function is similar to `local_waker`, but does not require that `wake` + /// is local to the current thread. The resulting `LocalWaker` will call + /// `wake.wake()` when awoken. + #[inline] + pub fn local_waker_from_nonlocal(wake: Arc) -> LocalWaker { + unsafe { + let ptr = mem::transmute::, NonNull>>(wake); + LocalWaker::new(ptr) + } + } +} diff --git a/src/libcore/future.rs b/src/libcore/future.rs new file mode 100644 index 00000000000..b4d087f8edb --- /dev/null +++ b/src/libcore/future.rs @@ -0,0 +1,93 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +//! Asynchronous values. + +use mem::PinMut; +use task::{self, Poll}; + +/// A future represents an asychronous computation. +/// +/// A future is a value that may not have finished computing yet. This kind of +/// "asynchronous value" makes it possible for a thread to continue doing useful +/// work while it waits for the value to become available. +/// +/// # The `poll` method +/// +/// The core method of future, `poll`, *attempts* to resolve the future into a +/// final value. This method does not block if the value is not ready. Instead, +/// the current task is scheduled to be woken up when it's possible to make +/// further progress by `poll`ing again. The wake up is performed using +/// `cx.waker()`, a handle for waking up the current task. +/// +/// When using a future, you generally won't call `poll` directly, but instead +/// `await!` the value. +pub trait Future { + /// The result of the `Future`. + type Output; + + /// Attempt to resolve the future to a final value, registering + /// the current task for wakeup if the value is not yet available. + /// + /// # Return value + /// + /// This function returns: + /// + /// - `Poll::Pending` if the future is not ready yet + /// - `Poll::Ready(val)` with the result `val` of this future if it finished + /// successfully. + /// + /// Once a future has finished, clients should not `poll` it again. + /// + /// When a future is not ready yet, `poll` returns + /// [`Poll::Pending`](::task::Poll). The future will *also* register the + /// interest of the current task in the value being produced. For example, + /// if the future represents the availability of data on a socket, then the + /// task is recorded so that when data arrives, it is woken up (via + /// [`cx.waker()`](::task::Context::waker)). Once a task has been woken up, + /// it should attempt to `poll` the future again, which may or may not + /// produce a final value. + /// + /// Note that if `Pending` is returned it only means that the *current* task + /// (represented by the argument `cx`) will receive a notification. Tasks + /// from previous calls to `poll` will *not* receive notifications. + /// + /// # Runtime characteristics + /// + /// Futures alone are *inert*; they must be *actively* `poll`ed to make + /// progress, meaning that each time the current task is woken up, it should + /// actively re-`poll` pending futures that it still has an interest in. + /// + /// The `poll` function is not called repeatedly in a tight loop for + /// futures, but only whenever the future itself is ready, as signaled via + /// the `Waker` inside `task::Context`. If you're familiar with the + /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures + /// typically do *not* suffer the same problems of "all wakeups must poll + /// all events"; they are more like `epoll(4)`. + /// + /// An implementation of `poll` should strive to return quickly, and must + /// *never* block. Returning quickly prevents unnecessarily clogging up + /// threads or event loops. If it is known ahead of time that a call to + /// `poll` may end up taking awhile, the work should be offloaded to a + /// thread pool (or something similar) to ensure that `poll` can return + /// quickly. + /// + /// # Panics + /// + /// Once a future has completed (returned `Ready` from `poll`), + /// then any future calls to `poll` may panic, block forever, or otherwise + /// cause bad behavior. The `Future` trait itself provides no guarantees + /// about the behavior of `poll` after a future has completed. + fn poll(self: PinMut, cx: &mut task::Context) -> Poll; +} diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 32cf31231c3..38a769cd11a 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -100,6 +100,7 @@ #![feature(optin_builtin_traits)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] +#![feature(repr_transparent)] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(simd_ffi)] @@ -206,6 +207,10 @@ pub mod time; pub mod unicode; +/* Async */ +pub mod future; +pub mod task; + /* Heap memory allocator trait */ #[allow(missing_docs)] pub mod alloc; diff --git a/src/libcore/task.rs b/src/libcore/task.rs new file mode 100644 index 00000000000..e46a6d41d7a --- /dev/null +++ b/src/libcore/task.rs @@ -0,0 +1,513 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +//! Types and Traits for working with asynchronous tasks. + +use fmt; +use ptr::NonNull; + +/// Indicates whether a value is available or if the current task has been +/// scheduled to receive a wakeup instead. +#[derive(Copy, Clone, Debug, Eq, PartialEq, Ord, PartialOrd, Hash)] +pub enum Poll { + /// Represents that a value is immediately ready. + Ready(T), + + /// Represents that a value is not ready yet. + /// + /// When a function returns `Pending`, the function *must* also + /// ensure that the current task is scheduled to be awoken when + /// progress can be made. + Pending, +} + +/// A `Waker` is a handle for waking up a task by notifying its executor that it +/// is ready to be run. +/// +/// This handle contains a trait object pointing to an instance of the `UnsafeWake` +/// trait, allowing notifications to get routed through it. +#[repr(transparent)] +pub struct Waker { + inner: NonNull, +} + +unsafe impl Send for Waker {} +unsafe impl Sync for Waker {} + +impl Waker { + /// Constructs a new `Waker` directly. + /// + /// Note that most code will not need to call this. Implementers of the + /// `UnsafeWake` trait will typically provide a wrapper that calls this + /// but you otherwise shouldn't call it directly. + /// + /// If you're working with the standard library then it's recommended to + /// use the `Waker::from` function instead which works with the safe + /// `Arc` type and the safe `Wake` trait. + #[inline] + pub unsafe fn new(inner: NonNull) -> Self { + Waker { inner: inner } + } + + /// Wake up the task associated with this `Waker`. + #[inline] + pub fn wake(&self) { + unsafe { self.inner.as_ref().wake() } + } + + /// Returns whether or not this `Waker` and `other` awaken the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns true, it is guaranteed that the `Waker`s will awaken the same + /// task. + /// + /// This function is primarily used for optimization purposes. + #[inline] + pub fn will_wake(&self, other: &Waker) -> bool { + self.inner == other.inner + } +} + +impl Clone for Waker { + #[inline] + fn clone(&self) -> Self { + unsafe { + self.inner.as_ref().clone_raw() + } + } +} + +impl fmt::Debug for Waker { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Waker") + .finish() + } +} + +impl Drop for Waker { + #[inline] + fn drop(&mut self) { + unsafe { + self.inner.as_ref().drop_raw() + } + } +} + +/// A `LocalWaker` is a handle for waking up a task by notifying its executor that it +/// is ready to be run. +/// +/// This is similar to the `Waker` type, but cannot be sent across threads. +/// Task executors can use this type to implement more optimized singlethreaded wakeup +/// behavior. +#[repr(transparent)] +pub struct LocalWaker { + inner: NonNull, +} + +impl !Send for LocalWaker {} +impl !Sync for LocalWaker {} + +impl LocalWaker { + /// Constructs a new `LocalWaker` directly. + /// + /// Note that most code will not need to call this. Implementers of the + /// `UnsafeWake` trait will typically provide a wrapper that calls this + /// but you otherwise shouldn't call it directly. + /// + /// If you're working with the standard library then it's recommended to + /// use the `LocalWaker::from` function instead which works with the safe + /// `Rc` type and the safe `LocalWake` trait. + /// + /// For this function to be used safely, it must be sound to call `inner.wake_local()` + /// on the current thread. + #[inline] + pub unsafe fn new(inner: NonNull) -> Self { + LocalWaker { inner: inner } + } + + /// Wake up the task associated with this `LocalWaker`. + #[inline] + pub fn wake(&self) { + unsafe { self.inner.as_ref().wake_local() } + } + + /// Returns whether or not this `LocalWaker` and `other` `LocalWaker` awaken the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `LocalWaker`s would awaken the same task. However, if this function + /// returns true, it is guaranteed that the `LocalWaker`s will awaken the same + /// task. + /// + /// This function is primarily used for optimization purposes. + #[inline] + pub fn will_wake(&self, other: &LocalWaker) -> bool { + self.inner == other.inner + } + + /// Returns whether or not this `LocalWaker` and `other` `Waker` awaken the same task. + /// + /// This function works on a best-effort basis, and may return false even + /// when the `Waker`s would awaken the same task. However, if this function + /// returns true, it is guaranteed that the `LocalWaker`s will awaken the same + /// task. + /// + /// This function is primarily used for optimization purposes. + #[inline] + pub fn will_wake_nonlocal(&self, other: &Waker) -> bool { + self.inner == other.inner + } +} + +impl From for Waker { + #[inline] + fn from(local_waker: LocalWaker) -> Self { + Waker { inner: local_waker.inner } + } +} + +impl Clone for LocalWaker { + #[inline] + fn clone(&self) -> Self { + unsafe { + LocalWaker { inner: self.inner.as_ref().clone_raw().inner } + } + } +} + +impl fmt::Debug for LocalWaker { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Waker") + .finish() + } +} + +impl Drop for LocalWaker { + #[inline] + fn drop(&mut self) { + unsafe { + self.inner.as_ref().drop_raw() + } + } +} + +/// An unsafe trait for implementing custom memory management for a `Waker` or `LocalWaker`. +/// +/// A `Waker` conceptually is a cloneable trait object for `Wake`, and is +/// most often essentially just `Arc`. However, in some contexts +/// (particularly `no_std`), it's desirable to avoid `Arc` in favor of some +/// custom memory management strategy. This trait is designed to allow for such +/// customization. +/// +/// When using `std`, a default implementation of the `UnsafeWake` trait is provided for +/// `Arc` where `T: Wake` and `Rc` where `T: LocalWake`. +/// +/// Although the methods on `UnsafeWake` take pointers rather than references, +pub unsafe trait UnsafeWake: Send + Sync { + /// Creates a clone of this `UnsafeWake` and stores it behind a `Waker`. + /// + /// This function will create a new uniquely owned handle that under the + /// hood references the same notification instance. In other words calls + /// to `wake` on the returned handle should be equivalent to calls to + /// `wake` on this handle. + /// + /// # Unsafety + /// + /// This function is unsafe to call because it's asserting the `UnsafeWake` + /// value is in a consistent state, i.e. hasn't been dropped. + unsafe fn clone_raw(&self) -> Waker; + + /// Drops this instance of `UnsafeWake`, deallocating resources + /// associated with it. + /// + /// FIXME(cramertj) + /// This method is intended to have a signature such as: + /// + /// ```ignore (not-a-doctest) + /// fn drop_raw(self: *mut Self); + /// ``` + /// + /// Unfortunately in Rust today that signature is not object safe. + /// Nevertheless it's recommended to implement this function *as if* that + /// were its signature. As such it is not safe to call on an invalid + /// pointer, nor is the validity of the pointer guaranteed after this + /// function returns. + /// + /// # Unsafety + /// + /// This function is unsafe to call because it's asserting the `UnsafeWake` + /// value is in a consistent state, i.e. hasn't been dropped. + unsafe fn drop_raw(&self); + + /// Indicates that the associated task is ready to make progress and should + /// be `poll`ed. + /// + /// Executors generally maintain a queue of "ready" tasks; `wake` should place + /// the associated task onto this queue. + /// + /// # Panics + /// + /// Implementations should avoid panicking, but clients should also be prepared + /// for panics. + /// + /// # Unsafety + /// + /// This function is unsafe to call because it's asserting the `UnsafeWake` + /// value is in a consistent state, i.e. hasn't been dropped. + unsafe fn wake(&self); + + /// Indicates that the associated task is ready to make progress and should + /// be `poll`ed. This function is the same as `wake`, but can only be called + /// from the thread that this `UnsafeWake` is "local" to. This allows for + /// implementors to provide specialized wakeup behavior specific to the current + /// thread. This function is called by `LocalWaker::wake`. + /// + /// Executors generally maintain a queue of "ready" tasks; `wake_local` should place + /// the associated task onto this queue. + /// + /// # Panics + /// + /// Implementations should avoid panicking, but clients should also be prepared + /// for panics. + /// + /// # Unsafety + /// + /// This function is unsafe to call because it's asserting the `UnsafeWake` + /// value is in a consistent state, i.e. hasn't been dropped, and that the + /// `UnsafeWake` hasn't moved from the thread on which it was created. + unsafe fn wake_local(&self) { + self.wake() + } +} + +/// Information about the currently-running task. +/// +/// Contexts are always tied to the stack, since they are set up specifically +/// when performing a single `poll` step on a task. +pub struct Context<'a> { + local_waker: &'a LocalWaker, + executor: &'a mut Executor, +} + +impl<'a> fmt::Debug for Context<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("Context") + .finish() + } +} + +impl<'a> Context<'a> { + /// Create a new task `Context` with the provided `local_waker`, `waker`, and `executor`. + #[inline] + pub fn new(local_waker: &'a LocalWaker, executor: &'a mut Executor) -> Context<'a> { + Context { + local_waker, + executor, + } + } + + /// Get the `LocalWaker` associated with the current task. + #[inline] + pub fn local_waker(&self) -> &'a LocalWaker { + self.local_waker + } + + /// Get the `Waker` associated with the current task. + #[inline] + pub fn waker(&self) -> &'a Waker { + unsafe { &*(self.local_waker as *const LocalWaker as *const Waker) } + } + + /// Get the default executor associated with this task. + /// + /// This method is useful primarily if you want to explicitly handle + /// spawn failures. + #[inline] + pub fn executor(&mut self) -> &mut Executor { + self.executor + } + + /// Produce a context like the current one, but using the given waker instead. + /// + /// This advanced method is primarily used when building "internal + /// schedulers" within a task, where you want to provide some customized + /// wakeup logic. + #[inline] + pub fn with_waker<'b>(&'b mut self, local_waker: &'b LocalWaker) -> Context<'b> { + Context { + local_waker, + executor: self.executor, + } + } + + /// Produce a context like the current one, but using the given executor + /// instead. + /// + /// This advanced method is primarily used when building "internal + /// schedulers" within a task. + #[inline] + pub fn with_executor<'b, E>(&'b mut self, executor: &'b mut E) -> Context<'b> + where E: Executor + { + Context { + local_waker: self.local_waker, + executor: executor, + } + } +} + +/// A task executor. +/// +/// A *task* is a `()`-producing async value that runs at the top level, and will +/// be `poll`ed until completion. It's also the unit at which wake-up +/// notifications occur. Executors, such as thread pools, allow tasks to be +/// spawned and are responsible for putting tasks onto ready queues when +/// they are woken up, and polling them when they are ready. +pub trait Executor { + /// Spawn the given task, polling it until completion. + /// + /// # Errors + /// + /// The executor may be unable to spawn tasks, either because it has + /// been shut down or is resource-constrained. + fn spawn_obj(&mut self, task: TaskObj) -> Result<(), SpawnObjError>; + + /// Determine whether the executor is able to spawn new tasks. + /// + /// # Returns + /// + /// An `Ok` return means the executor is *likely* (but not guaranteed) + /// to accept a subsequent spawn attempt. Likewise, an `Err` return + /// means that `spawn` is likely, but not guaranteed, to yield an error. + #[inline] + fn status(&self) -> Result<(), SpawnErrorKind> { + Ok(()) + } +} + +/// A custom trait object for polling tasks, roughly akin to +/// `Box + Send>`. +pub struct TaskObj { + ptr: *mut (), + poll: unsafe fn(*mut (), &mut Context) -> Poll<()>, + drop: unsafe fn(*mut ()), +} + +impl fmt::Debug for TaskObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("TaskObj") + .finish() + } +} + +unsafe impl Send for TaskObj {} +unsafe impl Sync for TaskObj {} + +/// A custom implementation of a task trait object for `TaskObj`, providing +/// a hand-rolled vtable. +/// +/// This custom representation is typically used only in `no_std` contexts, +/// where the default `Box`-based implementation is not available. +/// +/// The implementor must guarantee that it is safe to call `poll` repeatedly (in +/// a non-concurrent fashion) with the result of `into_raw` until `drop` is +/// called. +pub unsafe trait UnsafePoll: Send + 'static { + /// Convert a owned instance into a (conceptually owned) void pointer. + fn into_raw(self) -> *mut (); + + /// Poll the task represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to repeatedly call + /// `poll` with the result of `into_raw` until `drop` is called; such calls + /// are not, however, allowed to race with each other or with calls to `drop`. + unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()>; + + /// Drops the task represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to call this + /// function once per `into_raw` invocation; that call cannot race with + /// other calls to `drop` or `poll`. + unsafe fn drop(task: *mut ()); +} + +impl TaskObj { + /// Create a `TaskObj` from a custom trait object representation. + #[inline] + pub fn from_poll_task(t: T) -> TaskObj { + TaskObj { + ptr: t.into_raw(), + poll: T::poll, + drop: T::drop, + } + } + + /// Poll the task. + /// + /// The semantics here are identical to that for futures, but unlike + /// futures only an `&mut self` reference is needed here. + #[inline] + pub fn poll_task(&mut self, cx: &mut Context) -> Poll<()> { + unsafe { + (self.poll)(self.ptr, cx) + } + } +} + +impl Drop for TaskObj { + fn drop(&mut self) { + unsafe { + (self.drop)(self.ptr) + } + } +} + +/// Provides the reason that an executor was unable to spawn. +pub struct SpawnErrorKind { + _hidden: (), +} + +impl fmt::Debug for SpawnErrorKind { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SpawnErrorKind") + .field(&"shutdown") + .finish() + } +} + +impl SpawnErrorKind { + /// Spawning is failing because the executor has been shut down. + pub fn shutdown() -> SpawnErrorKind { + SpawnErrorKind { _hidden: () } + } + + /// Check whether this error is the `shutdown` error. + pub fn is_shutdown(&self) -> bool { + true + } +} + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: TaskObj, +} diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index f7d06852f27..f7ad709e6e7 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -261,6 +261,7 @@ #![feature(float_from_str_radix)] #![feature(fn_traits)] #![feature(fnbox)] +#![feature(futures_api)] #![feature(hashmap_internals)] #![feature(heap_api)] #![feature(int_error_internals)] @@ -282,6 +283,7 @@ #![feature(panic_internals)] #![feature(panic_unwind)] #![feature(peek)] +#![feature(pin)] #![feature(placement_new_protocol)] #![feature(prelude_import)] #![feature(ptr_internals)] @@ -457,6 +459,20 @@ pub use core::u128; #[stable(feature = "core_hint", since = "1.27.0")] pub use core::hint; +#[unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] +pub mod task { + //! Types and Traits for working with asynchronous tasks. + pub use core::task::*; + pub use alloc_crate::task::*; +} + +#[unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] +pub use core::future; + pub mod f32; pub mod f64; diff --git a/src/test/run-pass/futures-api.rs b/src/test/run-pass/futures-api.rs new file mode 100644 index 00000000000..3b5a1725b66 --- /dev/null +++ b/src/test/run-pass/futures-api.rs @@ -0,0 +1,95 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![feature(arbitrary_self_types, futures_api, pin)] +#![allow(unused)] + +use std::boxed::PinBox; +use std::future::Future; +use std::mem::PinMut; +use std::rc::Rc; +use std::sync::{ + Arc, + atomic::{self, AtomicUsize}, +}; +use std::task::{ + Context, Poll, + Wake, Waker, LocalWaker, + Executor, TaskObj, SpawnObjError, + local_waker, local_waker_from_nonlocal, +}; + +struct Counter { + local_wakes: AtomicUsize, + nonlocal_wakes: AtomicUsize, +} + +impl Wake for Counter { + fn wake(this: &Arc) { + this.nonlocal_wakes.fetch_add(1, atomic::Ordering::SeqCst); + } + + unsafe fn wake_local(this: &Arc) { + this.local_wakes.fetch_add(1, atomic::Ordering::SeqCst); + } +} + +struct NoopExecutor; + +impl Executor for NoopExecutor { + fn spawn_obj(&mut self, _: TaskObj) -> Result<(), SpawnObjError> { + Ok(()) + } +} + +struct MyFuture; + +impl Future for MyFuture { + type Output = (); + fn poll(self: PinMut, cx: &mut Context) -> Poll { + // Ensure all the methods work appropriately + cx.waker().wake(); + cx.waker().wake(); + cx.local_waker().wake(); + cx.executor().spawn_obj(PinBox::new(MyFuture).into()).unwrap(); + Poll::Ready(()) + } +} + +fn test_local_waker() { + let counter = Arc::new(Counter { + local_wakes: AtomicUsize::new(0), + nonlocal_wakes: AtomicUsize::new(0), + }); + let waker = unsafe { local_waker(counter.clone()) }; + let executor = &mut NoopExecutor; + let cx = &mut Context::new(&waker, executor); + assert_eq!(Poll::Ready(()), PinMut::new(&mut MyFuture).poll(cx)); + assert_eq!(1, counter.local_wakes.load(atomic::Ordering::SeqCst)); + assert_eq!(2, counter.nonlocal_wakes.load(atomic::Ordering::SeqCst)); +} + +fn test_local_as_nonlocal_waker() { + let counter = Arc::new(Counter { + local_wakes: AtomicUsize::new(0), + nonlocal_wakes: AtomicUsize::new(0), + }); + let waker: LocalWaker = local_waker_from_nonlocal(counter.clone()); + let executor = &mut NoopExecutor; + let cx = &mut Context::new(&waker, executor); + assert_eq!(Poll::Ready(()), PinMut::new(&mut MyFuture).poll(cx)); + assert_eq!(0, counter.local_wakes.load(atomic::Ordering::SeqCst)); + assert_eq!(3, counter.nonlocal_wakes.load(atomic::Ordering::SeqCst)); +} + +fn main() { + test_local_waker(); + test_local_as_nonlocal_waker(); +} -- cgit 1.4.1-3-g733a5 From 6e5c18e8dc94a679126d276884a3ad4b9a3e0934 Mon Sep 17 00:00:00 2001 From: tinaun Date: Fri, 8 Jun 2018 16:45:27 -0400 Subject: add a few blanket future impls to std --- src/liballoc/boxed.rs | 39 +++++++++++++++++++++++++++++++++++++++ src/liballoc/lib.rs | 1 + src/libcore/future.rs | 17 +++++++++++++++++ src/libcore/task.rs | 6 ++++++ src/libstd/lib.rs | 1 + src/libstd/panic.rs | 18 ++++++++++++++++++ 6 files changed, 82 insertions(+) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index a64b94b6517..896d9dee3ee 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -914,6 +914,45 @@ impl, U: ?Sized> CoerceUnsized> for PinBox {} #[unstable(feature = "pin", issue = "49150")] impl Unpin for PinBox {} +#[unstable(feature = "futures_api", issue = "50547")] +impl<'a, F: ?Sized + Future + Unpin> Future for Box { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut Context) -> Poll { + PinMut::new(&mut **self).poll(cx) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +impl<'a, F: ?Sized + Future> Future for PinBox { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut Context) -> Poll { + self.as_pin_mut().poll(cx) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +unsafe impl + Send + 'static> UnsafePoll for Box { + fn into_raw(self) -> *mut () { + unsafe { + mem::transmute(self) + } + } + + unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()> { + let ptr: *mut F = mem::transmute(task); + let pin: PinMut = PinMut::new_unchecked(&mut *ptr); + pin.poll(cx) + } + + unsafe fn drop(task: *mut ()) { + let ptr: *mut F = mem::transmute(task); + let boxed = Box::from_raw(ptr); + drop(boxed) + } +} + #[unstable(feature = "futures_api", issue = "50547")] unsafe impl + Send + 'static> UnsafePoll for PinBox { fn into_raw(self) -> *mut () { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 242c7d2e70f..a1139189c9a 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -80,6 +80,7 @@ #![cfg_attr(test, feature(rand, test))] #![feature(allocator_api)] #![feature(allow_internal_unstable)] +#![feature(arbitrary_self_types)] #![feature(ascii_ctype)] #![feature(box_into_raw_non_null)] #![feature(box_patterns)] diff --git a/src/libcore/future.rs b/src/libcore/future.rs index b4d087f8edb..a8c8f69411e 100644 --- a/src/libcore/future.rs +++ b/src/libcore/future.rs @@ -15,6 +15,7 @@ //! Asynchronous values. use mem::PinMut; +use marker::Unpin; use task::{self, Poll}; /// A future represents an asychronous computation. @@ -91,3 +92,19 @@ pub trait Future { /// about the behavior of `poll` after a future has completed. fn poll(self: PinMut, cx: &mut task::Context) -> Poll; } + +impl<'a, F: ?Sized + Future + Unpin> Future for &'a mut F { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { + F::poll(PinMut::new(&mut **self), cx) + } +} + +impl<'a, F: ?Sized + Future> Future for PinMut<'a, F> { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { + F::poll((*self).reborrow(), cx) + } +} diff --git a/src/libcore/task.rs b/src/libcore/task.rs index e46a6d41d7a..ab1c1da5790 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -32,6 +32,12 @@ pub enum Poll { Pending, } +impl From for Poll { + fn from(t: T) -> Poll { + Poll::Ready(t) + } +} + /// A `Waker` is a handle for waking up a task by notifying its executor that it /// is ready to be run. /// diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 7bbc99b83be..bb23fe5fa91 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -239,6 +239,7 @@ #![feature(allow_internal_unsafe)] #![feature(allow_internal_unstable)] #![feature(align_offset)] +#![feature(arbitrary_self_types)] #![feature(array_error_internals)] #![feature(ascii_ctype)] #![feature(asm)] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 229034eb779..b70de73991f 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -15,11 +15,14 @@ use any::Any; use cell::UnsafeCell; use fmt; +use future::Future; +use mem::PinMut; use ops::{Deref, DerefMut}; use panicking; use ptr::{Unique, NonNull}; use rc::Rc; use sync::{Arc, Mutex, RwLock, atomic}; +use task::{self, Poll}; use thread::Result; #[stable(feature = "panic_hooks", since = "1.10.0")] @@ -315,6 +318,21 @@ impl fmt::Debug for AssertUnwindSafe { } } +#[unstable(feature = "futures_api", issue = "50547")] +impl<'a, F: Future> Future for AssertUnwindSafe { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { + unsafe { + let pinned_field = PinMut::new_unchecked( + &mut PinMut::get_mut(self.reborrow()).0 + ); + + pinned_field.poll(cx) + } + } +} + /// Invokes a closure, capturing the cause of an unwinding panic if one occurs. /// /// This function will return `Ok` with the closure's result if the closure -- cgit 1.4.1-3-g733a5 From 49eb754cc0108d8546eae70cdcebf81aaddbece3 Mon Sep 17 00:00:00 2001 From: tinaun Date: Fri, 8 Jun 2018 23:16:51 -0400 Subject: addressed nits --- src/liballoc/boxed.rs | 21 --------------------- src/libstd/panic.rs | 6 +++--- 2 files changed, 3 insertions(+), 24 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 896d9dee3ee..c794fb8220a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -932,27 +932,6 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } } -#[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + Send + 'static> UnsafePoll for Box { - fn into_raw(self) -> *mut () { - unsafe { - mem::transmute(self) - } - } - - unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()> { - let ptr: *mut F = mem::transmute(task); - let pin: PinMut = PinMut::new_unchecked(&mut *ptr); - pin.poll(cx) - } - - unsafe fn drop(task: *mut ()) { - let ptr: *mut F = mem::transmute(task); - let boxed = Box::from_raw(ptr); - drop(boxed) - } -} - #[unstable(feature = "futures_api", issue = "50547")] unsafe impl + Send + 'static> UnsafePoll for PinBox { fn into_raw(self) -> *mut () { diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index b70de73991f..8aee15b5eda 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -326,9 +326,9 @@ impl<'a, F: Future> Future for AssertUnwindSafe { unsafe { let pinned_field = PinMut::new_unchecked( &mut PinMut::get_mut(self.reborrow()).0 - ); - - pinned_field.poll(cx) + ); + + pinned_field.poll(cx) } } } -- cgit 1.4.1-3-g733a5 From 861c7cb9fd37fc171f80a9f0b58f995ad67c6df0 Mon Sep 17 00:00:00 2001 From: Guillaume Gomez Date: Sat, 26 May 2018 11:11:17 +0200 Subject: Stabilize entry-or-default --- .../unstable-book/src/library-features/entry-or-default.md | 13 ------------- src/liballoc/btree/map.rs | 3 +-- src/librustc/lib.rs | 1 - src/libstd/collections/hash/map.rs | 4 +--- 4 files changed, 2 insertions(+), 19 deletions(-) delete mode 100644 src/doc/unstable-book/src/library-features/entry-or-default.md (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/library-features/entry-or-default.md b/src/doc/unstable-book/src/library-features/entry-or-default.md deleted file mode 100644 index f8c8a2a7a71..00000000000 --- a/src/doc/unstable-book/src/library-features/entry-or-default.md +++ /dev/null @@ -1,13 +0,0 @@ -# `entry_or_default` - -The tracking issue for this feature is: [#44324] - -[#44324]: https://github.com/rust-lang/rust/issues/44324 - ------------------------- - -The `entry_or_default` feature adds a new method to `hash_map::Entry` -and `btree_map::Entry`, `or_default`, when `V: Default`. This method is -semantically identical to `or_insert_with(Default::default)`, and will -insert the default value for the type if no entry exists for the current -key. diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs index 9b6f91c039f..e6e454446e2 100644 --- a/src/liballoc/btree/map.rs +++ b/src/liballoc/btree/map.rs @@ -2184,14 +2184,13 @@ impl<'a, K: Ord, V> Entry<'a, K, V> { } impl<'a, K: Ord, V: Default> Entry<'a, K, V> { - #[unstable(feature = "entry_or_default", issue = "44324")] + #[stable(feature = "entry_or_default", since = "1.28.0")] /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. /// /// # Examples /// /// ``` - /// #![feature(entry_or_default)] /// # fn main() { /// use std::collections::BTreeMap; /// diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index a006856f58b..102efe2bef3 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -45,7 +45,6 @@ #![feature(const_fn)] #![feature(core_intrinsics)] #![feature(drain_filter)] -#![feature(entry_or_default)] #![feature(from_ref)] #![feature(fs_read_write)] #![feature(iterator_find_map)] diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index 5cbd8891364..9c77acb83ec 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -2161,14 +2161,13 @@ impl<'a, K, V> Entry<'a, K, V> { } impl<'a, K, V: Default> Entry<'a, K, V> { - #[unstable(feature = "entry_or_default", issue = "44324")] + #[stable(feature = "entry_or_default", since = "1.28.0")] /// Ensures a value is in the entry by inserting the default value if empty, /// and returns a mutable reference to the value in the entry. /// /// # Examples /// /// ``` - /// #![feature(entry_or_default)] /// # fn main() { /// use std::collections::HashMap; /// @@ -2184,7 +2183,6 @@ impl<'a, K, V: Default> Entry<'a, K, V> { Vacant(entry) => entry.insert(Default::default()), } } - } impl<'a, K, V> OccupiedEntry<'a, K, V> { -- cgit 1.4.1-3-g733a5 From f6ab74b8e7efed01c1045773b6693f23f6ebd93c Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 31 May 2018 15:57:43 +0900 Subject: Remove alloc::Opaque and use *mut u8 as pointer type for GlobalAlloc --- .../src/language-features/global-allocator.md | 6 +- src/liballoc/alloc.rs | 33 ++++---- src/liballoc/arc.rs | 6 +- src/liballoc/btree/node.rs | 10 +-- src/liballoc/heap.rs | 12 +-- src/liballoc/raw_vec.rs | 19 +++-- src/liballoc/rc.rs | 10 +-- src/liballoc_system/lib.rs | 89 +++++++++++----------- src/libcore/alloc.rs | 65 ++++++---------- src/libcore/ptr.rs | 8 -- src/librustc_allocator/expand.rs | 15 +--- src/libstd/alloc.rs | 10 +-- src/libstd/collections/hash/table.rs | 2 +- src/test/run-make-fulldeps/std-core-cycle/bar.rs | 4 +- src/test/run-pass/allocator/auxiliary/custom.rs | 6 +- src/test/run-pass/allocator/custom.rs | 6 +- src/test/run-pass/realloc-16687.rs | 4 +- 17 files changed, 130 insertions(+), 175 deletions(-) (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/global-allocator.md b/src/doc/unstable-book/src/language-features/global-allocator.md index 8f1ba22de8c..7dfdc487731 100644 --- a/src/doc/unstable-book/src/language-features/global-allocator.md +++ b/src/doc/unstable-book/src/language-features/global-allocator.md @@ -29,17 +29,17 @@ looks like: ```rust #![feature(global_allocator, allocator_api, heap_api)] -use std::alloc::{GlobalAlloc, System, Layout, Opaque}; +use std::alloc::{GlobalAlloc, System, Layout}; use std::ptr::NonNull; struct MyAllocator; unsafe impl GlobalAlloc for MyAllocator { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { System.dealloc(ptr, layout) } } diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 8753c495737..102910f4198 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -51,52 +51,49 @@ pub const Heap: Global = Global; unsafe impl GlobalAlloc for Global { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - let ptr = __rust_alloc(layout.size(), layout.align()); - ptr as *mut Opaque + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + __rust_alloc(layout.size(), layout.align()) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { - __rust_dealloc(ptr as *mut u8, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - let ptr = __rust_realloc(ptr as *mut u8, layout.size(), layout.align(), new_size); - ptr as *mut Opaque + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + __rust_realloc(ptr, layout.size(), layout.align(), new_size) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - let ptr = __rust_alloc_zeroed(layout.size(), layout.align()); - ptr as *mut Opaque + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + __rust_alloc_zeroed(layout.size(), layout.align()) } } unsafe impl Alloc for Global { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) - -> Result, AllocErr> + -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } } @@ -113,7 +110,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); let ptr = Global.alloc(layout); if !ptr.is_null() { - ptr as *mut u8 + ptr } else { oom(layout) } @@ -129,7 +126,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut Opaque, layout); + Global.dealloc(ptr as *mut u8, layout); } } diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 4026b3ababa..e3369f0a5b5 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -519,7 +519,7 @@ impl Arc { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } } @@ -639,7 +639,7 @@ impl ArcFromSlice for Arc<[T]> { let slice = from_raw_parts_mut(self.elems, self.n_elems); ptr::drop_in_place(slice); - Global.dealloc(self.mem.as_opaque(), self.layout.clone()); + Global.dealloc(self.mem.cast(), self.layout.clone()); } } } @@ -1196,7 +1196,7 @@ impl Drop for Weak { if self.inner().weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())) + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) } } } diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs index 431695c32ab..19bdcbc6ad6 100644 --- a/src/liballoc/btree/node.rs +++ b/src/liballoc/btree/node.rs @@ -287,7 +287,7 @@ impl Root { self.as_mut().as_leaf_mut().parent = ptr::null(); unsafe { - Global.dealloc(NonNull::from(top).as_opaque(), Layout::new::>()); + Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); } } } @@ -478,7 +478,7 @@ impl NodeRef { debug_assert!(!self.is_shared_root()); let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_opaque(), Layout::new::>()); + Global.dealloc(node.cast(), Layout::new::>()); ret } } @@ -499,7 +499,7 @@ impl NodeRef { > { let node = self.node; let ret = self.ascend().ok(); - Global.dealloc(node.as_opaque(), Layout::new::>()); + Global.dealloc(node.cast(), Layout::new::>()); ret } } @@ -1321,12 +1321,12 @@ impl<'a, K, V> Handle, K, V, marker::Internal>, marker:: } Global.dealloc( - right_node.node.as_opaque(), + right_node.node.cast(), Layout::new::>(), ); } else { Global.dealloc( - right_node.node.as_opaque(), + right_node.node.cast(), Layout::new::>(), ); } diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs index 16f0630b911..5ea37ceeb2b 100644 --- a/src/liballoc/heap.rs +++ b/src/liballoc/heap.rs @@ -10,7 +10,7 @@ #![allow(deprecated)] -pub use alloc::{Layout, AllocErr, CannotReallocInPlace, Opaque}; +pub use alloc::{Layout, AllocErr, CannotReallocInPlace}; use core::alloc::Alloc as CoreAlloc; use core::ptr::NonNull; @@ -54,7 +54,7 @@ unsafe impl Alloc for T where T: CoreAlloc { } unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::dealloc(self, ptr, layout) } @@ -70,7 +70,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) } @@ -87,7 +87,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) } @@ -96,7 +96,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) } @@ -104,7 +104,7 @@ unsafe impl Alloc for T where T: CoreAlloc { ptr: *mut u8, layout: Layout, new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr as *mut Opaque); + let ptr = NonNull::new_unchecked(ptr); CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) } } diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 07bb7f1a3eb..c09f21eeb92 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -93,7 +93,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - NonNull::::dangling().as_opaque() + NonNull::::dangling().cast() } else { let align = mem::align_of::(); let layout = Layout::from_size_align(alloc_size, align).unwrap(); @@ -314,7 +314,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - let ptr_res = self.a.realloc(NonNull::from(self.ptr).as_opaque(), + let ptr_res = self.a.realloc(NonNull::from(self.ptr).cast(), cur, new_size); match ptr_res { @@ -373,7 +373,7 @@ impl RawVec { let new_cap = 2 * self.cap; let new_size = new_cap * elem_size; alloc_guard(new_size).unwrap_or_else(|_| capacity_overflow()); - match self.a.grow_in_place(NonNull::from(self.ptr).as_opaque(), old_layout, new_size) { + match self.a.grow_in_place(NonNull::from(self.ptr).cast(), old_layout, new_size) { Ok(_) => { // We can't directly divide `size`. self.cap = new_cap; @@ -546,7 +546,7 @@ impl RawVec { // FIXME: may crash and burn on over-reserve alloc_guard(new_layout.size()).unwrap_or_else(|_| capacity_overflow()); match self.a.grow_in_place( - NonNull::from(self.ptr).as_opaque(), old_layout, new_layout.size(), + NonNull::from(self.ptr).cast(), old_layout, new_layout.size(), ) { Ok(_) => { self.cap = new_cap; @@ -607,7 +607,7 @@ impl RawVec { let new_size = elem_size * amount; let align = mem::align_of::(); let old_layout = Layout::from_size_align_unchecked(old_size, align); - match self.a.realloc(NonNull::from(self.ptr).as_opaque(), + match self.a.realloc(NonNull::from(self.ptr).cast(), old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), @@ -667,7 +667,7 @@ impl RawVec { let res = match self.current_layout() { Some(layout) => { debug_assert!(new_layout.align() == layout.align()); - self.a.realloc(NonNull::from(self.ptr).as_opaque(), layout, new_layout.size()) + self.a.realloc(NonNull::from(self.ptr).cast(), layout, new_layout.size()) } None => self.a.alloc(new_layout), }; @@ -710,7 +710,7 @@ impl RawVec { let elem_size = mem::size_of::(); if elem_size != 0 { if let Some(layout) = self.current_layout() { - self.a.dealloc(NonNull::from(self.ptr).as_opaque(), layout); + self.a.dealloc(NonNull::from(self.ptr).cast(), layout); } } } @@ -753,7 +753,6 @@ fn capacity_overflow() -> ! { #[cfg(test)] mod tests { use super::*; - use alloc::Opaque; #[test] fn allocator_param() { @@ -773,7 +772,7 @@ mod tests { // before allocation attempts start failing. struct BoundedAlloc { fuel: usize } unsafe impl Alloc for BoundedAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); if size > self.fuel { return Err(AllocErr); @@ -783,7 +782,7 @@ mod tests { err @ Err(_) => err, } } - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { Global.dealloc(ptr, layout) } } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 553c8b5ca32..84a6ecf7103 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, Opaque, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, oom}; use string::String; use vec::Vec; @@ -732,7 +732,7 @@ impl RcFromSlice for Rc<[T]> { // In the event of a panic, elements that have been written // into the new RcBox will be dropped, then the memory freed. struct Guard { - mem: NonNull, + mem: NonNull, elems: *mut T, layout: Layout, n_elems: usize, @@ -755,7 +755,7 @@ impl RcFromSlice for Rc<[T]> { let v_ptr = v as *const [T]; let ptr = Self::allocate_for_ptr(v_ptr); - let mem = ptr as *mut _ as *mut Opaque; + let mem = ptr as *mut _ as *mut u8; let layout = Layout::for_value(&*ptr); // Pointer to first element @@ -839,7 +839,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Rc { self.dec_weak(); if self.weak() == 0 { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } } @@ -1263,7 +1263,7 @@ impl Drop for Weak { // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. if self.weak() == 0 { - Global.dealloc(self.ptr.as_opaque(), Layout::for_value(self.ptr.as_ref())); + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); } } } diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 9490b54e675..82fda8d639e 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -41,7 +41,7 @@ const MIN_ALIGN: usize = 8; #[allow(dead_code)] const MIN_ALIGN: usize = 16; -use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout, Opaque}; +use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout}; use core::ptr::NonNull; #[unstable(feature = "allocator_api", issue = "32838")] @@ -50,45 +50,45 @@ pub struct System; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for System { #[inline] - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) } #[inline] - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) } #[inline] unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } } #[cfg(any(windows, unix, target_os = "cloudabi", target_os = "redox"))] mod realloc_fallback { - use core::alloc::{GlobalAlloc, Opaque, Layout}; + use core::alloc::{GlobalAlloc, Layout}; use core::cmp; use core::ptr; impl super::System { - pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut Opaque, old_layout: Layout, - new_size: usize) -> *mut Opaque { + pub(crate) unsafe fn realloc_fallback(&self, ptr: *mut u8, old_layout: Layout, + new_size: usize) -> *mut u8 { // Docs for GlobalAlloc::realloc require this to be valid: let new_layout = Layout::from_size_align_unchecked(new_size, old_layout.align()); let new_ptr = GlobalAlloc::alloc(self, new_layout); if !new_ptr.is_null() { let size = cmp::min(old_layout.size(), new_size); - ptr::copy_nonoverlapping(ptr as *mut u8, new_ptr as *mut u8, size); + ptr::copy_nonoverlapping(ptr, new_ptr, size); GlobalAlloc::dealloc(self, ptr, old_layout); } new_ptr @@ -104,21 +104,19 @@ mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Layout, Opaque}; + use core::alloc::{GlobalAlloc, Layout}; #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::malloc(layout.size()) as *mut Opaque + libc::malloc(layout.size()) as *mut u8 } else { #[cfg(target_os = "macos")] { if layout.align() > (1 << 31) { - // FIXME: use Opaque::null_mut - // https://github.com/rust-lang/rust/issues/49659 - return 0 as *mut Opaque + return ptr::null_mut() } } aligned_malloc(&layout) @@ -126,27 +124,27 @@ mod platform { } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= layout.size() { - libc::calloc(layout.size(), 1) as *mut Opaque + libc::calloc(layout.size(), 1) as *mut u8 } else { let ptr = self.alloc(layout.clone()); if !ptr.is_null() { - ptr::write_bytes(ptr as *mut u8, 0, layout.size()); + ptr::write_bytes(ptr, 0, layout.size()); } ptr } } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, _layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, _layout: Layout) { libc::free(ptr as *mut libc::c_void) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN && layout.align() <= new_size { - libc::realloc(ptr as *mut libc::c_void, new_size) as *mut Opaque + libc::realloc(ptr as *mut libc::c_void, new_size) as *mut u8 } else { self.realloc_fallback(ptr, layout, new_size) } @@ -155,7 +153,7 @@ mod platform { #[cfg(any(target_os = "android", target_os = "redox", target_os = "solaris"))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { // On android we currently target API level 9 which unfortunately // doesn't have the `posix_memalign` API used below. Instead we use // `memalign`, but this unfortunately has the property on some systems @@ -173,19 +171,18 @@ mod platform { // [3]: https://bugs.chromium.org/p/chromium/issues/detail?id=138579 // [4]: https://chromium.googlesource.com/chromium/src/base/+/master/ // /memory/aligned_memory.cc - libc::memalign(layout.align(), layout.size()) as *mut Opaque + libc::memalign(layout.align(), layout.size()) as *mut u8 } #[cfg(not(any(target_os = "android", target_os = "redox", target_os = "solaris")))] #[inline] - unsafe fn aligned_malloc(layout: &Layout) -> *mut Opaque { + unsafe fn aligned_malloc(layout: &Layout) -> *mut u8 { let mut out = ptr::null_mut(); let ret = libc::posix_memalign(&mut out, layout.align(), layout.size()); if ret != 0 { - // FIXME: use Opaque::null_mut https://github.com/rust-lang/rust/issues/49659 - 0 as *mut Opaque + ptr::null_mut() } else { - out as *mut Opaque + out as *mut u8 } } } @@ -195,7 +192,7 @@ mod platform { mod platform { use MIN_ALIGN; use System; - use core::alloc::{GlobalAlloc, Opaque, Layout}; + use core::alloc::{GlobalAlloc, Layout}; type LPVOID = *mut u8; type HANDLE = LPVOID; @@ -227,7 +224,7 @@ mod platform { } #[inline] - unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut Opaque { + unsafe fn allocate_with_flags(layout: Layout, flags: DWORD) -> *mut u8 { let ptr = if layout.align() <= MIN_ALIGN { HeapAlloc(GetProcessHeap(), flags, layout.size()) } else { @@ -239,29 +236,29 @@ mod platform { align_ptr(ptr, layout.align()) } }; - ptr as *mut Opaque + ptr as *mut u8 } #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { allocate_with_flags(layout, 0) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { allocate_with_flags(layout, HEAP_ZERO_MEMORY) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { if layout.align() <= MIN_ALIGN { let err = HeapFree(GetProcessHeap(), 0, ptr as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); } else { - let header = get_header(ptr as *mut u8); + let header = get_header(ptr); let err = HeapFree(GetProcessHeap(), 0, header.0 as LPVOID); debug_assert!(err != 0, "Failed to free heap memory: {}", GetLastError()); @@ -269,9 +266,9 @@ mod platform { } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { if layout.align() <= MIN_ALIGN { - HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut Opaque + HeapReAlloc(GetProcessHeap(), 0, ptr as LPVOID, new_size) as *mut u8 } else { self.realloc_fallback(ptr, layout, new_size) } @@ -300,7 +297,7 @@ mod platform { mod platform { extern crate dlmalloc; - use core::alloc::{GlobalAlloc, Layout, Opaque}; + use core::alloc::{GlobalAlloc, Layout}; use System; // No need for synchronization here as wasm is currently single-threaded @@ -309,23 +306,23 @@ mod platform { #[unstable(feature = "allocator_api", issue = "32838")] unsafe impl GlobalAlloc for System { #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { - DLMALLOC.malloc(layout.size(), layout.align()) as *mut Opaque + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { + DLMALLOC.malloc(layout.size(), layout.align()) } #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { - DLMALLOC.calloc(layout.size(), layout.align()) as *mut Opaque + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { + DLMALLOC.calloc(layout.size(), layout.align()) } #[inline] - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { - DLMALLOC.free(ptr as *mut u8, layout.size(), layout.align()) + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { + DLMALLOC.free(ptr, layout.size(), layout.align()) } #[inline] - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { - DLMALLOC.realloc(ptr as *mut u8, layout.size(), layout.align(), new_size) as *mut Opaque + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + DLMALLOC.realloc(ptr, layout.size(), layout.align(), new_size) } } } diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 229758803c8..2815ef6400d 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -22,30 +22,13 @@ use usize; use ptr::{self, NonNull}; use num::NonZeroUsize; -extern { - /// An opaque, unsized type. Used for pointers to allocated memory. - /// - /// This type can only be used behind a pointer like `*mut Opaque` or `ptr::NonNull`. - /// Such pointers are similar to C’s `void*` type. - pub type Opaque; -} - -impl Opaque { - /// Similar to `std::ptr::null`, which requires `T: Sized`. - pub fn null() -> *const Self { - 0 as _ - } - - /// Similar to `std::ptr::null_mut`, which requires `T: Sized`. - pub fn null_mut() -> *mut Self { - 0 as _ - } -} +#[cfg(stage0)] +pub type Opaque = u8; /// Represents the combination of a starting address and /// a total capacity of the returned block. #[derive(Debug)] -pub struct Excess(pub NonNull, pub usize); +pub struct Excess(pub NonNull, pub usize); fn size_align() -> (usize, usize) { (mem::size_of::(), mem::align_of::()) @@ -417,7 +400,7 @@ pub unsafe trait GlobalAlloc { /// # Safety /// /// **FIXME:** what are the exact requirements? - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque; + unsafe fn alloc(&self, layout: Layout) -> *mut u8; /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. /// @@ -425,13 +408,13 @@ pub unsafe trait GlobalAlloc { /// /// **FIXME:** what are the exact requirements? /// In particular around layout *fit*. (See docs for the `Alloc` trait.) - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout); + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); let ptr = self.alloc(layout); if !ptr.is_null() { - ptr::write_bytes(ptr as *mut u8, 0, size); + ptr::write_bytes(ptr, 0, size); } ptr } @@ -452,13 +435,13 @@ pub unsafe trait GlobalAlloc { /// /// **FIXME:** what are the exact requirements? /// In particular around layout *fit*. (See docs for the `Alloc` trait.) - unsafe fn realloc(&self, ptr: *mut Opaque, layout: Layout, new_size: usize) -> *mut Opaque { + unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let new_ptr = self.alloc(new_layout); if !new_ptr.is_null() { ptr::copy_nonoverlapping( - ptr as *const u8, - new_ptr as *mut u8, + ptr, + new_ptr, cmp::min(layout.size(), new_size), ); self.dealloc(ptr, layout); @@ -598,7 +581,7 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; + unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. /// @@ -615,7 +598,7 @@ pub unsafe trait Alloc { /// * In addition to fitting the block of memory `layout`, the /// alignment of the `layout` must match the alignment used /// to allocate that block of memory. - unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); + unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout); // == ALLOCATOR-SPECIFIC QUANTITIES AND LIMITS == // usable_size @@ -707,9 +690,9 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, - new_size: usize) -> Result, AllocErr> { + new_size: usize) -> Result, AllocErr> { let old_size = layout.size(); if new_size >= old_size { @@ -726,8 +709,8 @@ pub unsafe trait Alloc { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let result = self.alloc(new_layout); if let Ok(new_ptr) = result { - ptr::copy_nonoverlapping(ptr.as_ptr() as *const u8, - new_ptr.as_ptr() as *mut u8, + ptr::copy_nonoverlapping(ptr.as_ptr(), + new_ptr.as_ptr(), cmp::min(old_size, new_size)); self.dealloc(ptr, layout); } @@ -750,11 +733,11 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { + unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); if let Ok(p) = p { - ptr::write_bytes(p.as_ptr() as *mut u8, 0, size); + ptr::write_bytes(p.as_ptr(), 0, size); } p } @@ -799,7 +782,7 @@ pub unsafe trait Alloc { /// reallocation error are encouraged to call the allocator's `oom` /// method, rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -844,7 +827,7 @@ pub unsafe trait Alloc { /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -899,7 +882,7 @@ pub unsafe trait Alloc { /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, - ptr: NonNull, + ptr: NonNull, layout: Layout, new_size: usize) -> Result<(), CannotReallocInPlace> { let _ = ptr; // this default implementation doesn't care about the actual address. @@ -978,7 +961,7 @@ pub unsafe trait Alloc { { let k = Layout::new::(); if k.size() > 0 { - self.dealloc(ptr.as_opaque(), k); + self.dealloc(ptr.cast(), k); } } @@ -1066,7 +1049,7 @@ pub unsafe trait Alloc { match (Layout::array::(n_old), Layout::array::(n_new)) { (Ok(ref k_old), Ok(ref k_new)) if k_old.size() > 0 && k_new.size() > 0 => { debug_assert!(k_old.align() == k_new.align()); - self.realloc(ptr.as_opaque(), k_old.clone(), k_new.size()).map(NonNull::cast) + self.realloc(ptr.cast(), k_old.clone(), k_new.size()).map(NonNull::cast) } _ => { Err(AllocErr) @@ -1099,7 +1082,7 @@ pub unsafe trait Alloc { { match Layout::array::(n) { Ok(ref k) if k.size() > 0 => { - Ok(self.dealloc(ptr.as_opaque(), k.clone())) + Ok(self.dealloc(ptr.cast(), k.clone())) } _ => { Err(AllocErr) diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 39315d8f0c8..81a8b3ef047 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -2922,14 +2922,6 @@ impl NonNull { NonNull::new_unchecked(self.as_ptr() as *mut U) } } - - /// Cast to an `Opaque` pointer - #[unstable(feature = "allocator_api", issue = "32838")] - pub fn as_opaque(self) -> NonNull<::alloc::Opaque> { - unsafe { - NonNull::new_unchecked(self.as_ptr() as _) - } - } } #[stable(feature = "nonnull", since = "1.25.0")] diff --git a/src/librustc_allocator/expand.rs b/src/librustc_allocator/expand.rs index 497d5fdcac7..ec0676259ef 100644 --- a/src/librustc_allocator/expand.rs +++ b/src/librustc_allocator/expand.rs @@ -237,7 +237,7 @@ impl<'a> AllocFnFactory<'a> { let ident = ident(); args.push(self.cx.arg(self.span, ident, self.ptr_u8())); let arg = self.cx.expr_ident(self.span, ident); - self.cx.expr_cast(self.span, arg, self.ptr_opaque()) + self.cx.expr_cast(self.span, arg, self.ptr_u8()) } AllocatorTy::Usize => { @@ -281,17 +281,4 @@ impl<'a> AllocFnFactory<'a> { let ty_u8 = self.cx.ty_path(u8); self.cx.ty_ptr(self.span, ty_u8, Mutability::Mutable) } - - fn ptr_opaque(&self) -> P { - let opaque = self.cx.path( - self.span, - vec![ - self.core, - Ident::from_str("alloc"), - Ident::from_str("Opaque"), - ], - ); - let ty_opaque = self.cx.ty_path(opaque); - self.cx.ty_ptr(self.span, ty_opaque, Mutability::Mutable) - } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 3b1a3a439e7..ac7da5e9dba 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -74,7 +74,7 @@ pub extern fn rust_oom(layout: Layout) -> ! { #[doc(hidden)] #[allow(unused_attributes)] pub mod __default_lib_allocator { - use super::{System, Layout, GlobalAlloc, Opaque}; + use super::{System, Layout, GlobalAlloc}; // for symbol names src/librustc/middle/allocator.rs // for signatures src/librustc_allocator/lib.rs @@ -85,7 +85,7 @@ pub mod __default_lib_allocator { #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc(layout) as *mut u8 + System.alloc(layout) } #[no_mangle] @@ -93,7 +93,7 @@ pub mod __default_lib_allocator { pub unsafe extern fn __rdl_dealloc(ptr: *mut u8, size: usize, align: usize) { - System.dealloc(ptr as *mut Opaque, Layout::from_size_align_unchecked(size, align)) + System.dealloc(ptr, Layout::from_size_align_unchecked(size, align)) } #[no_mangle] @@ -103,13 +103,13 @@ pub mod __default_lib_allocator { align: usize, new_size: usize) -> *mut u8 { let old_layout = Layout::from_size_align_unchecked(old_size, align); - System.realloc(ptr as *mut Opaque, old_layout, new_size) as *mut u8 + System.realloc(ptr, old_layout, new_size) } #[no_mangle] #[rustc_std_internal_symbol] pub unsafe extern fn __rdl_alloc_zeroed(size: usize, align: usize) -> *mut u8 { let layout = Layout::from_size_align_unchecked(size, align); - System.alloc_zeroed(layout) as *mut u8 + System.alloc_zeroed(layout) } } diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index d997fb28d42..55f9f4f7cfe 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -1124,7 +1124,7 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for RawTable { let (layout, _) = calculate_layout::(self.capacity()) .unwrap_or_else(|_| unsafe { hint::unreachable_unchecked() }); unsafe { - Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).as_opaque(), layout); + Global.dealloc(NonNull::new_unchecked(self.hashes.ptr()).cast(), layout); // Remember how everything was allocated out of one buffer // during initialization? We only need one call to free here. } diff --git a/src/test/run-make-fulldeps/std-core-cycle/bar.rs b/src/test/run-make-fulldeps/std-core-cycle/bar.rs index 62fd2ade1ca..4b885e5e2bb 100644 --- a/src/test/run-make-fulldeps/std-core-cycle/bar.rs +++ b/src/test/run-make-fulldeps/std-core-cycle/bar.rs @@ -16,11 +16,11 @@ use std::alloc::*; pub struct A; unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, _: Layout) -> *mut Opaque { + unsafe fn alloc(&self, _: Layout) -> *mut u8 { loop {} } - unsafe fn dealloc(&self, _ptr: *mut Opaque, _: Layout) { + unsafe fn dealloc(&self, _ptr: *mut u8, _: Layout) { loop {} } } diff --git a/src/test/run-pass/allocator/auxiliary/custom.rs b/src/test/run-pass/allocator/auxiliary/custom.rs index 91f70aa83e8..02e86fa19f8 100644 --- a/src/test/run-pass/allocator/auxiliary/custom.rs +++ b/src/test/run-pass/allocator/auxiliary/custom.rs @@ -13,18 +13,18 @@ #![feature(heap_api, allocator_api)] #![crate_type = "rlib"] -use std::alloc::{GlobalAlloc, System, Layout, Opaque}; +use std::alloc::{GlobalAlloc, System, Layout}; use std::sync::atomic::{AtomicUsize, Ordering}; pub struct A(pub AtomicUsize); unsafe impl GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { self.0.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { self.0.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/allocator/custom.rs b/src/test/run-pass/allocator/custom.rs index 415d39a593e..5da13bd4cb9 100644 --- a/src/test/run-pass/allocator/custom.rs +++ b/src/test/run-pass/allocator/custom.rs @@ -15,7 +15,7 @@ extern crate helper; -use std::alloc::{self, Global, Alloc, System, Layout, Opaque}; +use std::alloc::{self, Global, Alloc, System, Layout}; use std::sync::atomic::{AtomicUsize, Ordering, ATOMIC_USIZE_INIT}; static HITS: AtomicUsize = ATOMIC_USIZE_INIT; @@ -23,12 +23,12 @@ static HITS: AtomicUsize = ATOMIC_USIZE_INIT; struct A; unsafe impl alloc::GlobalAlloc for A { - unsafe fn alloc(&self, layout: Layout) -> *mut Opaque { + unsafe fn alloc(&self, layout: Layout) -> *mut u8 { HITS.fetch_add(1, Ordering::SeqCst); System.alloc(layout) } - unsafe fn dealloc(&self, ptr: *mut Opaque, layout: Layout) { + unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { HITS.fetch_add(1, Ordering::SeqCst); System.dealloc(ptr, layout) } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index febd249d776..355053858cc 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Global.dealloc(NonNull::new_unchecked(ptr).as_opaque(), layout); + Global.dealloc(NonNull::new_unchecked(ptr).cast(), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,7 +72,7 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).as_opaque(), old, new.size()) + let ret = Global.realloc(NonNull::new_unchecked(ptr).cast(), old, new.size()) .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); if PRINT { -- cgit 1.4.1-3-g733a5 From 3373204ac49ebdb7194020ea9c556ce87910f7b7 Mon Sep 17 00:00:00 2001 From: Mike Hommey Date: Thu, 31 May 2018 16:10:01 +0900 Subject: Replace `impl GlobalAlloc for Global` with a set of free functions --- src/liballoc/alloc.rs | 48 ++++++++++++++++-------------- src/libstd/alloc.rs | 1 + src/test/run-pass/allocator/xcrate-use2.rs | 6 ++-- 3 files changed, 29 insertions(+), 26 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 102910f4198..8c2dda77226 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -49,37 +49,39 @@ pub type Heap = Global; #[allow(non_upper_case_globals)] pub const Heap: Global = Global; -unsafe impl GlobalAlloc for Global { - #[inline] - unsafe fn alloc(&self, layout: Layout) -> *mut u8 { - __rust_alloc(layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn alloc(layout: Layout) -> *mut u8 { + __rust_alloc(layout.size(), layout.align()) +} - #[inline] - unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout) { - __rust_dealloc(ptr, layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { + __rust_dealloc(ptr, layout.size(), layout.align()) +} - #[inline] - unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { - __rust_realloc(ptr, layout.size(), layout.align(), new_size) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { + __rust_realloc(ptr, layout.size(), layout.align(), new_size) +} - #[inline] - unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { - __rust_alloc_zeroed(layout.size(), layout.align()) - } +#[unstable(feature = "allocator_api", issue = "32838")] +#[inline] +pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { + __rust_alloc_zeroed(layout.size(), layout.align()) } unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc(self, layout)).ok_or(AllocErr) + NonNull::new(alloc(layout)).ok_or(AllocErr) } #[inline] unsafe fn dealloc(&mut self, ptr: NonNull, layout: Layout) { - GlobalAlloc::dealloc(self, ptr.as_ptr(), layout) + dealloc(ptr.as_ptr(), layout) } #[inline] @@ -89,12 +91,12 @@ unsafe impl Alloc for Global { new_size: usize) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::realloc(self, ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) + NonNull::new(realloc(ptr.as_ptr(), layout, new_size)).ok_or(AllocErr) } #[inline] unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { - NonNull::new(GlobalAlloc::alloc_zeroed(self, layout)).ok_or(AllocErr) + NonNull::new(alloc_zeroed(layout)).ok_or(AllocErr) } } @@ -108,7 +110,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { align as *mut u8 } else { let layout = Layout::from_size_align_unchecked(size, align); - let ptr = Global.alloc(layout); + let ptr = alloc(layout); if !ptr.is_null() { ptr } else { @@ -126,7 +128,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { // We do not allocate for Box when T is ZST, so deallocation is also not necessary. if size != 0 { let layout = Layout::from_size_align_unchecked(size, align); - Global.dealloc(ptr as *mut u8, layout); + dealloc(ptr as *mut u8, layout); } } diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ac7da5e9dba..3f31fa8e1dd 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -14,6 +14,7 @@ #[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; #[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; +#[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; #[doc(inline)] pub use alloc_system::System; #[doc(inline)] pub use core::alloc::*; diff --git a/src/test/run-pass/allocator/xcrate-use2.rs b/src/test/run-pass/allocator/xcrate-use2.rs index b8e844522dc..fbde7e855c2 100644 --- a/src/test/run-pass/allocator/xcrate-use2.rs +++ b/src/test/run-pass/allocator/xcrate-use2.rs @@ -19,7 +19,7 @@ extern crate custom; extern crate custom_as_global; extern crate helper; -use std::alloc::{Global, Alloc, GlobalAlloc, System, Layout}; +use std::alloc::{alloc, dealloc, GlobalAlloc, System, Layout}; use std::sync::atomic::{Ordering, ATOMIC_USIZE_INIT}; static GLOBAL: custom::A = custom::A(ATOMIC_USIZE_INIT); @@ -30,10 +30,10 @@ fn main() { let layout = Layout::from_size_align(4, 2).unwrap(); // Global allocator routes to the `custom_as_global` global - let ptr = Global.alloc(layout.clone()); + let ptr = alloc(layout.clone()); helper::work_with(&ptr); assert_eq!(custom_as_global::get(), n + 1); - Global.dealloc(ptr, layout.clone()); + dealloc(ptr, layout.clone()); assert_eq!(custom_as_global::get(), n + 2); // Usage of the system allocator avoids all globals -- cgit 1.4.1-3-g733a5 From 8c30c5168694b8ee740dade3a0145eef8aba66c6 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 30 May 2018 20:46:59 +0200 Subject: Remove deprecated heap modules The heap.rs file was already unused. --- src/liballoc/heap.rs | 110 ------------------------------------------ src/liballoc/lib.rs | 8 --- src/libcore/lib.rs | 7 --- src/libstd/collections/mod.rs | 2 +- src/libstd/error.rs | 2 +- src/libstd/lib.rs | 7 --- 6 files changed, 2 insertions(+), 134 deletions(-) delete mode 100644 src/liballoc/heap.rs (limited to 'src/liballoc') diff --git a/src/liballoc/heap.rs b/src/liballoc/heap.rs deleted file mode 100644 index 5ea37ceeb2b..00000000000 --- a/src/liballoc/heap.rs +++ /dev/null @@ -1,110 +0,0 @@ -// Copyright 2014-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![allow(deprecated)] - -pub use alloc::{Layout, AllocErr, CannotReallocInPlace}; -use core::alloc::Alloc as CoreAlloc; -use core::ptr::NonNull; - -#[doc(hidden)] -pub mod __core { - pub use core::*; -} - -#[derive(Debug)] -pub struct Excess(pub *mut u8, pub usize); - -/// Compatibility with older versions of #[global_allocator] during bootstrap -pub unsafe trait Alloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout); - fn oom(&mut self, err: AllocErr) -> !; - fn usable_size(&self, layout: &Layout) -> (usize, usize); - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr>; - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr>; - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result; - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result; - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace>; - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace>; -} - -unsafe impl Alloc for T where T: CoreAlloc { - unsafe fn alloc(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc(self, layout).map(|ptr| ptr.cast().as_ptr()) - } - - unsafe fn dealloc(&mut self, ptr: *mut u8, layout: Layout) { - let ptr = NonNull::new_unchecked(ptr); - CoreAlloc::dealloc(self, ptr, layout) - } - - fn oom(&mut self, _: AllocErr) -> ! { - unsafe { ::core::intrinsics::abort() } - } - - fn usable_size(&self, layout: &Layout) -> (usize, usize) { - CoreAlloc::usable_size(self, layout) - } - - unsafe fn realloc(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<*mut u8, AllocErr> { - let ptr = NonNull::new_unchecked(ptr); - CoreAlloc::realloc(self, ptr, layout, new_layout.size()).map(|ptr| ptr.cast().as_ptr()) - } - - unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result<*mut u8, AllocErr> { - CoreAlloc::alloc_zeroed(self, layout).map(|ptr| ptr.cast().as_ptr()) - } - - unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { - CoreAlloc::alloc_excess(self, layout) - .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) - } - - unsafe fn realloc_excess(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result { - let ptr = NonNull::new_unchecked(ptr); - CoreAlloc::realloc_excess(self, ptr, layout, new_layout.size()) - .map(|e| Excess(e.0 .cast().as_ptr(), e.1)) - } - - unsafe fn grow_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr); - CoreAlloc::grow_in_place(self, ptr, layout, new_layout.size()) - } - - unsafe fn shrink_in_place(&mut self, - ptr: *mut u8, - layout: Layout, - new_layout: Layout) -> Result<(), CannotReallocInPlace> { - let ptr = NonNull::new_unchecked(ptr); - CoreAlloc::shrink_in_place(self, ptr, layout, new_layout.size()) - } -} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 242c7d2e70f..828461fe8d7 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -150,18 +150,10 @@ pub mod allocator { pub mod alloc; -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -/// Use the `alloc` module instead. -pub mod heap { - pub use alloc::*; -} - #[unstable(feature = "futures_api", reason = "futures in libcore are unstable", issue = "50547")] pub mod task; - // Primitive types using the heaps above // Need to conditionally define the mod from `boxed.rs` to avoid diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index a2ee0033872..5ba77edee6e 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -215,13 +215,6 @@ pub mod task; #[allow(missing_docs)] pub mod alloc; -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -/// Use the `alloc` module instead. -pub mod heap { - pub use alloc::*; -} - // note: does not need to be public mod iter_private; mod nonzero; diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index d8e79b97970..42113414183 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -438,7 +438,7 @@ pub use self::hash_map::HashMap; pub use self::hash_set::HashSet; #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -pub use heap::CollectionAllocErr; +pub use alloc::CollectionAllocErr; mod hash; diff --git a/src/libstd/error.rs b/src/libstd/error.rs index 817eea5eaf1..3160485375f 100644 --- a/src/libstd/error.rs +++ b/src/libstd/error.rs @@ -23,13 +23,13 @@ // coherence challenge (e.g., specialization, neg impls, etc) we can // reconsider what crate these items belong in. +use alloc::{AllocErr, LayoutErr, CannotReallocInPlace}; use any::TypeId; use borrow::Cow; use cell; use char; use core::array; use fmt::{self, Debug, Display}; -use heap::{AllocErr, LayoutErr, CannotReallocInPlace}; use mem::transmute; use num; use str; diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index 4bf52224ae6..3972763a051 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -499,13 +499,6 @@ pub mod process; pub mod sync; pub mod time; -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "module renamed to `alloc`")] -/// Use the `alloc` module instead. -pub mod heap { - pub use alloc::*; -} - // Platform-abstraction modules #[macro_use] mod sys_common; -- cgit 1.4.1-3-g733a5 From 11f992c9584f05f56664627ac1ec42e4cd1f0e3e Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 30 May 2018 21:15:40 +0200 Subject: Remove the deprecated Heap type/const --- src/liballoc/alloc.rs | 9 --------- src/libstd/alloc.rs | 1 - 2 files changed, 10 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 8c2dda77226..710da9a475b 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -40,15 +40,6 @@ extern "Rust" { #[derive(Copy, Clone, Default, Debug)] pub struct Global; -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -pub type Heap = Global; - -#[unstable(feature = "allocator_api", issue = "32838")] -#[rustc_deprecated(since = "1.27.0", reason = "type renamed to `Global`")] -#[allow(non_upper_case_globals)] -pub const Heap: Global = Global; - #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 3f31fa8e1dd..9904634f1fa 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -12,7 +12,6 @@ #![unstable(issue = "32838", feature = "allocator_api")] -#[doc(inline)] #[allow(deprecated)] pub use alloc_crate::alloc::Heap; #[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; #[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; #[doc(inline)] pub use alloc_system::System; -- cgit 1.4.1-3-g733a5 From 0081d8826b00f4eaf217d4d68d54e715bd98dcb9 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 30 May 2018 21:04:17 +0200 Subject: Remove some unneeded casts --- src/liballoc/raw_vec.rs | 6 +++--- src/test/run-pass/realloc-16687.rs | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index c09f21eeb92..d1f140e96a3 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -93,7 +93,7 @@ impl RawVec { // handles ZSTs and `cap = 0` alike let ptr = if alloc_size == 0 { - NonNull::::dangling().cast() + NonNull::::dangling() } else { let align = mem::align_of::(); let layout = Layout::from_size_align(alloc_size, align).unwrap(); @@ -103,13 +103,13 @@ impl RawVec { a.alloc(layout) }; match result { - Ok(ptr) => ptr, + Ok(ptr) => ptr.cast(), Err(_) => oom(layout), } }; RawVec { - ptr: ptr.cast().into(), + ptr: ptr.into(), cap, a, } diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 355053858cc..7152e721eac 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -64,7 +64,7 @@ unsafe fn test_triangle() -> bool { println!("deallocate({:?}, {:?}", ptr, layout); } - Global.dealloc(NonNull::new_unchecked(ptr).cast(), layout); + Global.dealloc(NonNull::new_unchecked(ptr), layout); } unsafe fn reallocate(ptr: *mut u8, old: Layout, new: Layout) -> *mut u8 { @@ -72,7 +72,7 @@ unsafe fn test_triangle() -> bool { println!("reallocate({:?}, old={:?}, new={:?})", ptr, old, new); } - let ret = Global.realloc(NonNull::new_unchecked(ptr).cast(), old, new.size()) + let ret = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); if PRINT { -- cgit 1.4.1-3-g733a5 From e9fd063edb4f6783fbd91a82a0f61626dacf8dad Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 18:23:42 +0200 Subject: Document memory allocation APIs Add some docs where they were missing, attempt to fix them where they were out of date. --- src/liballoc/alloc.rs | 65 +++++++++++++ src/liballoc_system/lib.rs | 1 + src/libcore/alloc.rs | 227 +++++++++++++++++++++++++++++++++++---------- src/libstd/alloc.rs | 2 +- 4 files changed, 245 insertions(+), 50 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 710da9a475b..c9430a29e4c 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Memory allocation APIs + #![unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked \ slightly, especially to possibly take into account the \ @@ -37,27 +39,80 @@ extern "Rust" { fn __rust_alloc_zeroed(size: usize, align: usize) -> *mut u8; } +/// The global memory allocator. +/// +/// This type implements the [`Alloc`] trait by forwarding calls +/// to the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. #[derive(Copy, Clone, Default, Debug)] pub struct Global; +/// Allocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { __rust_alloc(layout.size(), layout.align()) } +/// Deallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::dealloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `dealloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::dealloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { __rust_dealloc(ptr, layout.size(), layout.align()) } +/// Reallocate memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::realloc`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `realloc` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::realloc`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { __rust_realloc(ptr, layout.size(), layout.align(), new_size) } +/// Allocate zero-initialized memory with the global allocator. +/// +/// This function forwards calls to the [`GlobalAlloc::alloc_zeroed`] method +/// of the allocator registered with the `#[global_allocator]` attribute +/// if there is one, or the `std` crate’s default. +/// +/// This function is expected to be deprecated in favor of the `alloc_zeroed` method +/// of the [`Global`] type when it and the [`Alloc`] trait become stable. +/// +/// # Safety +/// +/// See [`GlobalAlloc::alloc_zeroed`]. #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { @@ -123,6 +178,16 @@ pub(crate) unsafe fn box_free(ptr: Unique) { } } +/// Abort on memory allocation error or failure. +/// +/// Callers of memory allocation APIs wishing to abort computation +/// in response to an allocation error are encouraged to call this function, +/// rather than directly invoking `panic!` or similar. +/// +/// The default behavior of this function is to print a message to standard error +/// and abort the process. +/// It can be replaced with [`std::alloc::set_oom_hook`] +/// and [`std::alloc::take_oom_hook`]. #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] diff --git a/src/liballoc_system/lib.rs b/src/liballoc_system/lib.rs index 82fda8d639e..85bf43a5429 100644 --- a/src/liballoc_system/lib.rs +++ b/src/liballoc_system/lib.rs @@ -44,6 +44,7 @@ const MIN_ALIGN: usize = 16; use core::alloc::{Alloc, GlobalAlloc, AllocErr, Layout}; use core::ptr::NonNull; +/// The default memory allocator provided by the operating system. #[unstable(feature = "allocator_api", issue = "32838")] pub struct System; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 2815ef6400d..8fcd8555cdc 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -8,6 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +//! Memory allocation APIs + #![unstable(feature = "allocator_api", reason = "the precise API and guarantees it provides may be tweaked \ slightly, especially to possibly take into account the \ @@ -315,8 +317,9 @@ impl Layout { } } -/// The parameters given to `Layout::from_size_align` do not satisfy -/// its documented constraints. +/// The parameters given to `Layout::from_size_align` +/// or some other `Layout` constructor +/// do not satisfy its documented constraints. #[derive(Clone, PartialEq, Eq, Debug)] pub struct LayoutErr { private: () @@ -329,8 +332,8 @@ impl fmt::Display for LayoutErr { } } -/// The `AllocErr` error specifies whether an allocation failure is -/// specifically due to resource exhaustion or if it is due to +/// The `AllocErr` error indicates an allocation failure +/// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. #[derive(Clone, PartialEq, Eq, Debug)] @@ -346,6 +349,7 @@ impl fmt::Display for AllocErr { /// The `CannotReallocInPlace` error is used when `grow_in_place` or /// `shrink_in_place` were unable to reuse the given memory block for /// a requested layout. +// FIXME: should this be in libcore or liballoc? #[derive(Clone, PartialEq, Eq, Debug)] pub struct CannotReallocInPlace; @@ -363,6 +367,7 @@ impl fmt::Display for CannotReallocInPlace { } /// Augments `AllocErr` with a CapacityOverflow variant. +// FIXME: should this be in libcore or liballoc? #[derive(Clone, PartialEq, Eq, Debug)] #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] pub enum CollectionAllocErr { @@ -389,27 +394,125 @@ impl From for CollectionAllocErr { } } -/// A memory allocator that can be registered to be the one backing `std::alloc::Global` +/// A memory allocator that can be registered as the standard library’s default /// though the `#[global_allocator]` attributes. +/// +/// Some of the methods require that a memory block be *currently +/// allocated* via an allocator. This means that: +/// +/// * the starting address for that memory block was previously +/// returned by a previous call to an allocation method +/// such as `alloc`, and +/// +/// * the memory block has not been subsequently deallocated, where +/// blocks are deallocated either by being passed to a deallocation +/// method such as `dealloc` or by being +/// passed to a reallocation method that returns a non-null pointer. +/// +/// +/// # Example +/// +/// ```no_run +/// use std::alloc::{GlobalAlloc, Layout, alloc}; +/// use std::ptr::null_mut; +/// +/// struct MyAllocator; +/// +/// unsafe impl GlobalAlloc for MyAllocator { +/// unsafe fn alloc(&self, _layout: Layout) -> *mut u8 { null_mut() } +/// unsafe fn dealloc(&self, _ptr: *mut u8, _layout: Layout) {} +/// } +/// +/// #[global_allocator] +/// static A: MyAllocator = MyAllocator; +/// +/// fn main() { +/// unsafe { +/// assert!(alloc(Layout::new::()).is_null()) +/// } +/// } +/// ``` +/// +/// # Unsafety +/// +/// The `GlobalAlloc` 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 `GlobalAlloc` 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. +/// +/// * `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. pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. /// /// Returns a pointer to newly-allocated memory, - /// or NULL to indicate allocation failure. + /// or null to indicate allocation failure. /// /// # Safety /// - /// **FIXME:** what are the exact requirements? + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure that `layout` has non-zero size. + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// The allocated block of memory may or may not be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet allocator's size or alignment constraints. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc(&self, layout: Layout) -> *mut u8; /// Deallocate the block of memory at the given `ptr` pointer with the given `layout`. /// /// # Safety /// - /// **FIXME:** what are the exact requirements? - /// In particular around layout *fit*. (See docs for the `Alloc` trait.) + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must denote a block of memory currently allocated via + /// this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocated that block of memory, unsafe fn dealloc(&self, ptr: *mut u8, layout: Layout); + /// Behaves like `alloc`, but also ensures that the contents + /// are set to zero before being returned. + /// + /// # Safety + /// + /// This function is unsafe for the same reasons that `alloc` is. + /// However the allocated block of memory is guaranteed to be initialized. + /// + /// # Errors + /// + /// Returning a null pointer indicates that either memory is exhausted + /// or `layout` does not meet allocator's size or alignment constraints, + /// just as in `alloc`. + /// + /// Clients wishing to abort computation in response to an + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); let ptr = self.alloc(layout); @@ -422,19 +525,51 @@ pub unsafe trait GlobalAlloc { /// Shink or grow a block of memory to the given `new_size`. /// The block is described by the given `ptr` pointer and `layout`. /// - /// Return a new pointer (which may or may not be the same as `ptr`), - /// or NULL to indicate reallocation failure. + /// If this returns a non-null pointer, then ownership of the memory block + /// referenced by `ptr` has been transferred to this alloctor. + /// The memory may or may not have been deallocated, + /// and should be considered unusable (unless of course it was + /// transferred back to the caller again via the return value of + /// this method). /// - /// If reallocation is successful, the old `ptr` pointer is considered - /// to have been deallocated. + /// If this method returns null, then ownership of the memory + /// block has not been transferred to this allocator, and the + /// contents of the memory block are unaltered. /// /// # Safety /// - /// `new_size`, when rounded up to the nearest multiple of `old_layout.align()`, - /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). + /// This function is unsafe because undefined behavior can result + /// if the caller does not ensure all of the following: + /// + /// * `ptr` must be currently allocated via this allocator, + /// + /// * `layout` must be the same layout that was used + /// to allocated that block of memory, + /// + /// * `new_size` must be greater than zero. + /// + /// * `new_size`, when rounded up to the nearest multiple of `layout.align()`, + /// must not overflow (i.e. the rounded value must be less than `usize::MAX`). + /// + /// (Extension subtraits might provide more specific bounds on + /// behavior, e.g. guarantee a sentinel address or a null pointer + /// in response to a zero-size allocation request.) + /// + /// # Errors + /// + /// Returns null if the new layout does not meet the size + /// and alignment constraints of the allocator, or if reallocation + /// otherwise fails. + /// + /// Implementations are encouraged to return null on memory + /// exhaustion rather than panicking or aborting, but this is not + /// a strict requirement. (Specifically: it is *legal* to + /// implement this trait atop an underlying native allocation + /// library that aborts on memory exhaustion.) /// - /// **FIXME:** what are the exact requirements? - /// In particular around layout *fit*. (See docs for the `Alloc` trait.) + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); let new_ptr = self.alloc(new_layout); @@ -523,27 +658,21 @@ pub unsafe trait GlobalAlloc { /// 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. +/// the future. pub unsafe trait Alloc { - // (Note: existing allocators have unspecified but well-defined + // (Note: some existing allocators have unspecified but well-defined // behavior in response to a zero size allocation request ; // e.g. in C, `malloc` of 0 will either return a null pointer or a // unique pointer, but will not have arbitrary undefined - // behavior. Rust should consider revising the alloc::heap crate - // to reflect this reality.) + // behavior. + // However in jemalloc for example, + // `mallocx(0)` is documented as undefined behavior.) /// Returns a pointer meeting the size and alignment guarantees of /// `layout`. @@ -579,8 +708,8 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the `oom` function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -686,9 +815,9 @@ pub unsafe trait Alloc { /// implement this trait atop an underlying native allocation /// library that aborts on memory exhaustion.) /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -731,8 +860,8 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -757,8 +886,8 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -778,9 +907,9 @@ pub unsafe trait Alloc { /// `layout` does not meet allocator's size or alignment /// constraints, just as in `realloc`. /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -823,7 +952,7 @@ pub unsafe trait Alloc { /// could fit `layout`. /// /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from + /// function; clients are expected either to be able to recover from /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. unsafe fn grow_in_place(&mut self, @@ -878,7 +1007,7 @@ pub unsafe trait Alloc { /// could fit `layout`. /// /// Note that one cannot pass `CannotReallocInPlace` to the `oom` - /// method; clients are expected either to be able to recover from + /// function; clients are expected either to be able to recover from /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. unsafe fn shrink_in_place(&mut self, @@ -926,8 +1055,8 @@ pub unsafe trait Alloc { /// will *not* yield undefined behavior. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -993,8 +1122,8 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// allocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1037,9 +1166,9 @@ pub unsafe trait Alloc { /// /// Always returns `Err` on arithmetic overflow. /// - /// Clients wishing to abort computation in response to an - /// reallocation error are encouraged to call the allocator's `oom` - /// method, rather than directly invoking `panic!` or similar. + /// Clients wishing to abort computation in response to a + /// reallocation error are encouraged to call the [`oom`] function, + /// rather than directly invoking `panic!` or similar. unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 9904634f1fa..9126155a7c9 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -//! dox +//! Memory allocation APIs #![unstable(issue = "32838", feature = "allocator_api")] -- cgit 1.4.1-3-g733a5 From 951bc28fd09f5fed793021c6be1645e034394491 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 18:36:51 +0200 Subject: Stablize the alloc module without changing stability of its contents. --- src/liballoc/alloc.rs | 11 ++++----- src/libcore/alloc.rs | 32 +++++++++++++++++++++----- src/libstd/alloc.rs | 19 ++++++++++----- src/test/compile-fail/lint-stability-fields.rs | 7 ++++++ 4 files changed, 51 insertions(+), 18 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index c9430a29e4c..fc3d0151ec0 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -10,17 +10,13 @@ //! Memory allocation APIs -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] +#![stable(feature = "alloc_module", since = "1.28.0")] use core::intrinsics::{min_align_of_val, size_of_val}; use core::ptr::{NonNull, Unique}; use core::usize; +#[stable(feature = "alloc_module", since = "1.28.0")] #[doc(inline)] pub use core::alloc::*; @@ -44,6 +40,7 @@ extern "Rust" { /// This type implements the [`Alloc`] trait by forwarding calls /// to the allocator registered with the `#[global_allocator]` attribute /// if there is one, or the `std` crate’s default. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Default, Debug)] pub struct Global; @@ -119,6 +116,7 @@ pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { __rust_alloc_zeroed(layout.size(), layout.align()) } +#[unstable(feature = "allocator_api", issue = "32838")] unsafe impl Alloc for Global { #[inline] unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr> { @@ -188,6 +186,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// and abort the process. /// It can be replaced with [`std::alloc::set_oom_hook`] /// and [`std::alloc::take_oom_hook`]. +#[unstable(feature = "allocator_api", issue = "32838")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 8fcd8555cdc..ae9f77d35cf 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -10,12 +10,7 @@ //! Memory allocation APIs -#![unstable(feature = "allocator_api", - reason = "the precise API and guarantees it provides may be tweaked \ - slightly, especially to possibly take into account the \ - types being stored to make room for a future \ - tracing garbage collector", - issue = "32838")] +#![stable(feature = "alloc_module", since = "1.28.0")] use cmp; use fmt; @@ -24,11 +19,13 @@ use usize; use ptr::{self, NonNull}; use num::NonZeroUsize; +#[unstable(feature = "allocator_api", issue = "32838")] #[cfg(stage0)] pub type Opaque = u8; /// Represents the combination of a starting address and /// a total capacity of the returned block. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Debug)] pub struct Excess(pub NonNull, pub usize); @@ -49,6 +46,7 @@ fn size_align() -> (usize, usize) { /// requests have positive size. A caller to the `Alloc::alloc` /// method must either ensure that conditions like this are met, or /// use specific allocators with looser requirements.) +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Copy, Clone, Debug, PartialEq, Eq)] pub struct Layout { // size of the requested block of memory, measured in bytes. @@ -74,6 +72,7 @@ impl Layout { /// * `size`, when rounded up to the nearest multiple of `align`, /// must not overflow (i.e. the rounded value must be less than /// `usize::MAX`). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn from_size_align(size: usize, align: usize) -> Result { if !align.is_power_of_two() { @@ -109,20 +108,24 @@ impl Layout { /// /// This function is unsafe as it does not verify the preconditions from /// [`Layout::from_size_align`](#method.from_size_align). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub unsafe fn from_size_align_unchecked(size: usize, align: usize) -> Self { Layout { size_: size, align_: NonZeroUsize::new_unchecked(align) } } /// The minimum size in bytes for a memory block of this layout. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn size(&self) -> usize { self.size_ } /// The minimum byte alignment for a memory block of this layout. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn align(&self) -> usize { self.align_.get() } /// Constructs a `Layout` suitable for holding a value of type `T`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn new() -> Self { let (size, align) = size_align::(); @@ -139,6 +142,7 @@ impl Layout { /// Produces layout describing a record that could be used to /// allocate backing structure for `T` (which could be a trait /// or other unsized type like a slice). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn for_value(t: &T) -> Self { let (size, align) = (mem::size_of_val(t), mem::align_of_val(t)); @@ -166,6 +170,7 @@ impl Layout { /// Panics if the combination of `self.size()` and the given `align` /// violates the conditions listed in /// [`Layout::from_size_align`](#method.from_size_align). + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn align_to(&self, align: usize) -> Self { Layout::from_size_align(self.size(), cmp::max(self.align(), align)).unwrap() @@ -187,6 +192,7 @@ impl Layout { /// to be less than or equal to the alignment of the starting /// address for the whole allocated block of memory. One way to /// satisfy this constraint is to ensure `align <= self.align()`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn padding_needed_for(&self, align: usize) -> usize { let len = self.size(); @@ -223,6 +229,7 @@ impl Layout { /// of each element in the array. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn repeat(&self, n: usize) -> Result<(Self, usize), LayoutErr> { let padded_size = self.size().checked_add(self.padding_needed_for(self.align())) @@ -248,6 +255,7 @@ impl Layout { /// (assuming that the record itself starts at offset 0). /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn extend(&self, next: Self) -> Result<(Self, usize), LayoutErr> { let new_align = cmp::max(self.align(), next.align()); @@ -274,6 +282,7 @@ impl Layout { /// aligned. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn repeat_packed(&self, n: usize) -> Result { let size = self.size().checked_mul(n).ok_or(LayoutErr { private: () })?; @@ -295,6 +304,7 @@ impl Layout { /// `extend`.) /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn extend_packed(&self, next: Self) -> Result<(Self, usize), LayoutErr> { let new_size = self.size().checked_add(next.size()) @@ -306,6 +316,7 @@ impl Layout { /// Creates a layout describing the record for a `[T; n]`. /// /// On arithmetic overflow, returns `LayoutErr`. + #[unstable(feature = "allocator_api", issue = "32838")] #[inline] pub fn array(n: usize) -> Result { Layout::new::() @@ -320,12 +331,14 @@ impl Layout { /// The parameters given to `Layout::from_size_align` /// or some other `Layout` constructor /// do not satisfy its documented constraints. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct LayoutErr { private: () } // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for LayoutErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("invalid parameters to Layout::from_size_align") @@ -336,10 +349,12 @@ impl fmt::Display for LayoutErr { /// that may be due to resource exhaustion or to /// something wrong when combining the given input arguments with this /// allocator. +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct AllocErr; // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for AllocErr { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { f.write_str("memory allocation failed") @@ -350,9 +365,11 @@ impl fmt::Display for AllocErr { /// `shrink_in_place` were unable to reuse the given memory block for /// a requested layout. // FIXME: should this be in libcore or liballoc? +#[unstable(feature = "allocator_api", issue = "32838")] #[derive(Clone, PartialEq, Eq, Debug)] pub struct CannotReallocInPlace; +#[unstable(feature = "allocator_api", issue = "32838")] impl CannotReallocInPlace { pub fn description(&self) -> &str { "cannot reallocate allocator's memory in place" @@ -360,6 +377,7 @@ impl CannotReallocInPlace { } // (we need this for downstream impl of trait Error) +#[unstable(feature = "allocator_api", issue = "32838")] impl fmt::Display for CannotReallocInPlace { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}", self.description()) @@ -449,6 +467,7 @@ impl From for CollectionAllocErr { /// * `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. +#[unstable(feature = "allocator_api", issue = "32838")] pub unsafe trait GlobalAlloc { /// Allocate memory as described by the given `layout`. /// @@ -664,6 +683,7 @@ pub unsafe trait GlobalAlloc { /// /// Note that this list may get tweaked over time as clarifications are made in /// the future. +#[unstable(feature = "allocator_api", issue = "32838")] pub unsafe trait Alloc { // (Note: some existing allocators have unspecified but well-defined diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index 9126155a7c9..1a4869f87c9 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -10,17 +10,20 @@ //! Memory allocation APIs -#![unstable(issue = "32838", feature = "allocator_api")] - -#[doc(inline)] pub use alloc_crate::alloc::{Global, Layout, oom}; -#[doc(inline)] pub use alloc_crate::alloc::{alloc, alloc_zeroed, dealloc, realloc}; -#[doc(inline)] pub use alloc_system::System; -#[doc(inline)] pub use core::alloc::*; +#![stable(feature = "alloc_module", since = "1.28.0")] use core::sync::atomic::{AtomicPtr, Ordering}; use core::{mem, ptr}; use sys_common::util::dumb_print; +#[stable(feature = "alloc_module", since = "1.28.0")] +#[doc(inline)] +pub use alloc_crate::alloc::*; + +#[unstable(feature = "allocator_api", issue = "32838")] +#[doc(inline)] +pub use alloc_system::System; + static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// Registers a custom OOM hook, replacing any that was previously registered. @@ -34,6 +37,7 @@ static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); /// about the allocation that failed. /// /// The OOM hook is a global resource. +#[unstable(feature = "allocator_api", issue = "32838")] pub fn set_oom_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); } @@ -43,6 +47,7 @@ pub fn set_oom_hook(hook: fn(Layout)) { /// *See also the function [`set_oom_hook`].* /// /// If no custom hook is registered, the default hook will be returned. +#[unstable(feature = "allocator_api", issue = "32838")] pub fn take_oom_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); if hook.is_null() { @@ -59,6 +64,7 @@ fn default_oom_hook(layout: Layout) { #[cfg(not(test))] #[doc(hidden)] #[lang = "oom"] +#[unstable(feature = "allocator_api", issue = "32838")] pub extern fn rust_oom(layout: Layout) -> ! { let hook = HOOK.load(Ordering::SeqCst); let hook: fn(Layout) = if hook.is_null() { @@ -73,6 +79,7 @@ pub extern fn rust_oom(layout: Layout) -> ! { #[cfg(not(test))] #[doc(hidden)] #[allow(unused_attributes)] +#[unstable(feature = "allocator_api", issue = "32838")] pub mod __default_lib_allocator { use super::{System, Layout, GlobalAlloc}; // for symbol names src/librustc/middle/allocator.rs diff --git a/src/test/compile-fail/lint-stability-fields.rs b/src/test/compile-fail/lint-stability-fields.rs index 1b605bdb893..b1b1a9a1fbf 100644 --- a/src/test/compile-fail/lint-stability-fields.rs +++ b/src/test/compile-fail/lint-stability-fields.rs @@ -18,6 +18,11 @@ mod cross_crate { extern crate lint_stability_fields; + mod reexport { + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::lint_stability_fields::*; + } + use self::lint_stability_fields::*; pub fn foo() { @@ -73,6 +78,8 @@ mod cross_crate { // the patterns are all fine: { .. } = x; + // Unstable items are still unstable even when used through a stable "pub use". + let x = reexport::Unstable2(1, 2, 3); //~ ERROR use of unstable let x = Unstable2(1, 2, 3); //~ ERROR use of unstable -- cgit 1.4.1-3-g733a5 From 45d6d207b00cb1353d0f66d4319f2bdcf1fbc892 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 19:13:37 +0200 Subject: Stabilize alloc free functions for the global allocators. --- src/liballoc/alloc.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index fc3d0151ec0..839b1bf99be 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -56,7 +56,7 @@ pub struct Global; /// # Safety /// /// See [`GlobalAlloc::alloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc(layout: Layout) -> *mut u8 { __rust_alloc(layout.size(), layout.align()) @@ -74,7 +74,7 @@ pub unsafe fn alloc(layout: Layout) -> *mut u8 { /// # Safety /// /// See [`GlobalAlloc::dealloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { __rust_dealloc(ptr, layout.size(), layout.align()) @@ -92,7 +92,7 @@ pub unsafe fn dealloc(ptr: *mut u8, layout: Layout) { /// # Safety /// /// See [`GlobalAlloc::realloc`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { __rust_realloc(ptr, layout.size(), layout.align(), new_size) @@ -110,7 +110,7 @@ pub unsafe fn realloc(ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 /// # Safety /// /// See [`GlobalAlloc::alloc_zeroed`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[inline] pub unsafe fn alloc_zeroed(layout: Layout) -> *mut u8 { __rust_alloc_zeroed(layout.size(), layout.align()) -- cgit 1.4.1-3-g733a5 From 125b259b3567f6370d5c242eb102ca0957ffe017 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 31 May 2018 19:13:57 +0200 Subject: Stabilize alloc::oom (but not set_oom_hook or take_oom_hook) --- src/liballoc/alloc.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 839b1bf99be..fef668d2365 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -186,7 +186,7 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// and abort the process. /// It can be replaced with [`std::alloc::set_oom_hook`] /// and [`std::alloc::take_oom_hook`]. -#[unstable(feature = "allocator_api", issue = "32838")] +#[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { #[allow(improper_ctypes)] -- cgit 1.4.1-3-g733a5 From 7f0d54d98842c786ab7a140c17c3ea32aea6aead Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 1 Jun 2018 09:18:25 +0200 Subject: More alloc docs tweaks --- src/liballoc/alloc.rs | 6 ++++-- src/libcore/alloc.rs | 24 +++++++++++++++++++++++- 2 files changed, 27 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index fef668d2365..04c8063ffeb 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -184,8 +184,10 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// /// The default behavior of this function is to print a message to standard error /// and abort the process. -/// It can be replaced with [`std::alloc::set_oom_hook`] -/// and [`std::alloc::take_oom_hook`]. +/// It can be replaced with [`set_oom_hook`] and [`take_oom_hook`]. +/// +/// [`set_oom_hook`]: ../../std/alloc/fn.set_oom_hook.html +/// [`take_oom_hook`]: ../../std/alloc/fn.take_oom_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] pub fn oom(layout: Layout) -> ! { diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index b0ac43e50f5..353688d1b85 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -494,6 +494,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc(&self, layout: Layout) -> *mut u8; @@ -529,6 +531,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); @@ -587,6 +591,8 @@ pub unsafe trait GlobalAlloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -727,8 +733,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the `oom` function, + /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -837,6 +845,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -881,6 +891,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -907,6 +919,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -929,6 +943,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -1076,6 +1092,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -1143,6 +1161,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to an /// allocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1188,6 +1208,8 @@ pub unsafe trait Alloc { /// Clients wishing to abort computation in response to a /// reallocation error are encouraged to call the [`oom`] function, /// rather than directly invoking `panic!` or similar. + /// + /// [`oom`]: ../../alloc/alloc/fn.oom.html unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, -- cgit 1.4.1-3-g733a5 From e2aef92c19a95d6a0b8e75b473023f77de6150f0 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 6 Jun 2018 13:24:16 +0200 Subject: Stabilize #[repr(transparent)] Tracking issue FCP: https://github.com/rust-lang/rust/issues/43036#issuecomment-394094318 Reference PR: https://github.com/rust-lang-nursery/reference/pull/353 --- .../src/language-features/repr-transparent.md | 176 --------------------- src/liballoc/lib.rs | 2 +- src/libcore/lib.rs | 2 +- src/librustc/diagnostics.rs | 8 - src/librustc_typeck/diagnostics.rs | 10 +- src/libsyntax/feature_gate.rs | 10 +- src/test/codegen/repr-transparent-aggregates-1.rs | 1 - src/test/codegen/repr-transparent-aggregates-2.rs | 1 - src/test/codegen/repr-transparent-aggregates-3.rs | 1 - src/test/codegen/repr-transparent-sysv64.rs | 1 - src/test/codegen/repr-transparent.rs | 2 +- .../compile-fail/repr-transparent-other-items.rs | 2 - .../compile-fail/repr-transparent-other-reprs.rs | 2 +- src/test/compile-fail/repr-transparent.rs | 1 - src/test/ui/feature-gate-repr_transparent.rs | 14 -- src/test/ui/feature-gate-repr_transparent.stderr | 11 -- src/test/ui/lint-ctypes.rs | 2 +- 17 files changed, 9 insertions(+), 237 deletions(-) delete mode 100644 src/doc/unstable-book/src/language-features/repr-transparent.md delete mode 100644 src/test/ui/feature-gate-repr_transparent.rs delete mode 100644 src/test/ui/feature-gate-repr_transparent.stderr (limited to 'src/liballoc') diff --git a/src/doc/unstable-book/src/language-features/repr-transparent.md b/src/doc/unstable-book/src/language-features/repr-transparent.md deleted file mode 100644 index 62202dc96fd..00000000000 --- a/src/doc/unstable-book/src/language-features/repr-transparent.md +++ /dev/null @@ -1,176 +0,0 @@ -# `repr_transparent` - -The tracking issue for this feature is: [#43036] - -[#43036]: https://github.com/rust-lang/rust/issues/43036 - ------------------------- - -This feature enables the `repr(transparent)` attribute on structs, which enables -the use of newtypes without the usual ABI implications of wrapping the value in -a struct. - -## Background - -It's sometimes useful to add additional type safety by introducing *newtypes*. -For example, code that handles numeric quantities in different units such as -millimeters, centimeters, grams, kilograms, etc. may want to use the type system -to rule out mistakes such as adding millimeters to grams: - -```rust -use std::ops::Add; - -struct Millimeters(f64); -struct Grams(f64); - -impl Add for Millimeters { - type Output = Millimeters; - - fn add(self, other: Millimeters) -> Millimeters { - Millimeters(self.0 + other.0) - } -} - -// Likewise: impl Add for Grams {} -``` - -Other uses of newtypes include using `PhantomData` to add lifetimes to raw -pointers or to implement the "phantom types" pattern. See the [PhantomData] -documentation and [the Nomicon][nomicon-phantom] for more details. - -The added type safety is especially useful when interacting with C or other -languages. However, in those cases we need to ensure the newtypes we add do not -introduce incompatibilities with the C ABI. - -## Newtypes in FFI - -Luckily, `repr(C)` newtypes are laid out just like the type they wrap on all -platforms which Rust currently supports, and likely on many more. For example, -consider this C declaration: - -```C -struct Object { - double weight; //< in grams - double height; //< in millimeters - // ... -} - -void frobnicate(struct Object *); -``` - -While using this C code from Rust, we could add `repr(C)` to the `Grams` and -`Millimeters` newtypes introduced above and use them to add some type safety -while staying compatible with the memory layout of `Object`: - -```rust,no_run -#[repr(C)] -struct Grams(f64); - -#[repr(C)] -struct Millimeters(f64); - -#[repr(C)] -struct Object { - weight: Grams, - height: Millimeters, - // ... -} - -extern { - fn frobnicate(_: *mut Object); -} -``` - -This works even when adding some `PhantomData` fields, because they are -zero-sized and therefore don't have to affect the memory layout. - -However, there's more to the ABI than just memory layout: there's also the -question of how function call arguments and return values are passed. Many -common ABI treat a struct containing a single field differently from that field -itself, at least when the field is a scalar (e.g., integer or float or pointer). - -To continue the above example, suppose the C library also exposes a function -like this: - -```C -double calculate_weight(double height); -``` - -Using our newtypes on the Rust side like this will cause an ABI mismatch on many -platforms: - -```rust,ignore -extern { - fn calculate_weight(height: Millimeters) -> Grams; -} -``` - -For example, on x86_64 Linux, Rust will pass the argument in an integer -register, while the C function expects the argument to be in a floating-point -register. Likewise, the C function will return the result in a floating-point -register while Rust will expect it in an integer register. - -Note that this problem is not specific to floats: To give another example, -32-bit x86 linux will pass and return `struct Foo(i32);` on the stack while -`i32` is placed in registers. - -## Enter `repr(transparent)` - -So while `repr(C)` happens to do the right thing with respect to memory layout, -it's not quite the right tool for newtypes in FFI. Instead of declaring a C -struct, we need to communicate to the Rust compiler that our newtype is just for -type safety on the Rust side. This is what `repr(transparent)` does. - -The attribute can be applied to a newtype-like structs that contains a single -field. It indicates that the newtype should be represented exactly like that -field's type, i.e., the newtype should be ignored for ABI purpopses: not only is -it laid out the same in memory, it is also passed identically in function calls. - -In the above example, the ABI mismatches can be prevented by making the newtypes -`Grams` and `Millimeters` transparent like this: - -```rust -#![feature(repr_transparent)] - -#[repr(transparent)] -struct Grams(f64); - -#[repr(transparent)] -struct Millimeters(f64); -``` - -In addition to that single field, any number of zero-sized fields are permitted, -including but not limited to `PhantomData`: - -```rust -#![feature(repr_transparent)] - -use std::marker::PhantomData; - -struct Foo { /* ... */ } - -#[repr(transparent)] -struct FooPtrWithLifetime<'a>(*const Foo, PhantomData<&'a Foo>); - -#[repr(transparent)] -struct NumberWithUnit(T, PhantomData); - -struct CustomZst; - -#[repr(transparent)] -struct PtrWithCustomZst<'a> { - ptr: FooPtrWithLifetime<'a>, - some_marker: CustomZst, -} -``` - -Transparent structs can be nested: `PtrWithCustomZst` is also represented -exactly like `*const Foo`. - -Because `repr(transparent)` delegates all representation concerns to another -type, it is incompatible with all other `repr(..)` attributes. It also cannot be -applied to enums, unions, empty structs, structs whose fields are all -zero-sized, or structs with *multiple* non-zero-sized fields. - -[PhantomData]: https://doc.rust-lang.org/std/marker/struct.PhantomData.html -[nomicon-phantom]: https://doc.rust-lang.org/nomicon/phantom-data.html diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 74bbd659246..e25742a4a61 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -105,7 +105,7 @@ #![feature(pin)] #![feature(ptr_internals)] #![feature(ptr_offset_from)] -#![feature(repr_transparent)] +#![cfg_attr(stage0, feature(repr_transparent))] #![feature(rustc_attrs)] #![feature(specialization)] #![feature(staged_api)] diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 5ba77edee6e..40caee85541 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -100,7 +100,7 @@ #![feature(optin_builtin_traits)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] -#![feature(repr_transparent)] +#![cfg_attr(stage0, feature(repr_transparent))] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(simd_ffi)] diff --git a/src/librustc/diagnostics.rs b/src/librustc/diagnostics.rs index 7415ddd455d..1435957a5c1 100644 --- a/src/librustc/diagnostics.rs +++ b/src/librustc/diagnostics.rs @@ -1958,8 +1958,6 @@ representation hints. Erroneous code example: ```compile_fail,E0692 -#![feature(repr_transparent)] - #[repr(transparent, C)] // error: incompatible representation hints struct Grams(f32); ``` @@ -1969,8 +1967,6 @@ another type, so adding more representation hints is contradictory. Remove either the `transparent` hint or the other hints, like this: ``` -#![feature(repr_transparent)] - #[repr(transparent)] struct Grams(f32); ``` @@ -1978,8 +1974,6 @@ struct Grams(f32); Alternatively, move the other attributes to the contained type: ``` -#![feature(repr_transparent)] - #[repr(C)] struct Foo { x: i32, @@ -1994,8 +1988,6 @@ Note that introducing another `struct` just to have a place for the other attributes may have unintended side effects on the representation: ``` -#![feature(repr_transparent)] - #[repr(transparent)] struct Grams(f32); diff --git a/src/librustc_typeck/diagnostics.rs b/src/librustc_typeck/diagnostics.rs index e4c73218de5..da54eeabdb9 100644 --- a/src/librustc_typeck/diagnostics.rs +++ b/src/librustc_typeck/diagnostics.rs @@ -4581,8 +4581,6 @@ on fields that were not guaranteed to be zero-sized. Erroneous code example: ```compile_fail,E0690 -#![feature(repr_transparent)] - #[repr(transparent)] struct LengthWithUnit { // error: transparent struct needs exactly one value: f32, // non-zero-sized field, but has 2 @@ -4602,8 +4600,6 @@ To combine `repr(transparent)` with type parameters, `PhantomData` may be useful: ``` -#![feature(repr_transparent)] - use std::marker::PhantomData; #[repr(transparent)] @@ -4621,7 +4617,7 @@ field that requires non-trivial alignment. Erroneous code example: ```compile_fail,E0691 -#![feature(repr_transparent, repr_align, attr_literals)] +#![feature(repr_align, attr_literals)] #[repr(align(32))] struct ForceAlign32; @@ -4640,8 +4636,6 @@ requirement. Consider removing the over-aligned zero-sized field: ``` -#![feature(repr_transparent)] - #[repr(transparent)] struct Wrapper(f32); ``` @@ -4650,7 +4644,7 @@ Alternatively, `PhantomData` has alignment 1 for all `T`, so you can use it if you need to keep the field for some reason: ``` -#![feature(repr_transparent, repr_align, attr_literals)] +#![feature(repr_align, attr_literals)] use std::marker::PhantomData; diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 1535e649506..9f370672cb2 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -399,9 +399,6 @@ declare_features! ( // `extern` in paths (active, extern_in_paths, "1.23.0", Some(44660), None), - // Allows `#[repr(transparent)]` attribute on newtype structs - (active, repr_transparent, "1.25.0", Some(43036), None), - // Use `?` as the Kleene "at most one" operator (active, macro_at_most_once_rep, "1.25.0", Some(48075), None), @@ -615,6 +612,8 @@ declare_features! ( (accepted, termination_trait_test, "1.27.0", Some(48854), None), // The #[global_allocator] attribute (accepted, global_allocator, "1.28.0", Some(27389), None), + // Allows `#[repr(transparent)]` attribute on newtype structs + (accepted, repr_transparent, "1.28.0", Some(43036), None), ); // If you change this, please modify src/doc/unstable-book as well. You must @@ -1595,11 +1594,6 @@ impl<'a> Visitor<'a> for PostExpansionVisitor<'a> { gate_feature_post!(&self, repr_simd, attr.span, "SIMD types are experimental and possibly buggy"); } - if item.check_name("transparent") { - gate_feature_post!(&self, repr_transparent, attr.span, - "the `#[repr(transparent)]` attribute \ - is experimental"); - } if let Some((name, _)) = item.name_value_literal() { if name == "packed" { gate_feature_post!(&self, repr_packed, attr.span, diff --git a/src/test/codegen/repr-transparent-aggregates-1.rs b/src/test/codegen/repr-transparent-aggregates-1.rs index 2eeed2b788c..a1185cc1e2e 100644 --- a/src/test/codegen/repr-transparent-aggregates-1.rs +++ b/src/test/codegen/repr-transparent-aggregates-1.rs @@ -18,7 +18,6 @@ // See repr-transparent.rs #![crate_type="lib"] -#![feature(repr_transparent)] #[repr(C)] diff --git a/src/test/codegen/repr-transparent-aggregates-2.rs b/src/test/codegen/repr-transparent-aggregates-2.rs index 25750a6513f..bc000bd3165 100644 --- a/src/test/codegen/repr-transparent-aggregates-2.rs +++ b/src/test/codegen/repr-transparent-aggregates-2.rs @@ -22,7 +22,6 @@ // See repr-transparent.rs #![crate_type="lib"] -#![feature(repr_transparent)] #[repr(C)] diff --git a/src/test/codegen/repr-transparent-aggregates-3.rs b/src/test/codegen/repr-transparent-aggregates-3.rs index 0c90239c9de..a292f1d70f3 100644 --- a/src/test/codegen/repr-transparent-aggregates-3.rs +++ b/src/test/codegen/repr-transparent-aggregates-3.rs @@ -14,7 +14,6 @@ // See repr-transparent.rs #![crate_type="lib"] -#![feature(repr_transparent)] #[repr(C)] diff --git a/src/test/codegen/repr-transparent-sysv64.rs b/src/test/codegen/repr-transparent-sysv64.rs index 7a30983fdd3..2e4665e22e3 100644 --- a/src/test/codegen/repr-transparent-sysv64.rs +++ b/src/test/codegen/repr-transparent-sysv64.rs @@ -13,7 +13,6 @@ // compile-flags: -C no-prepopulate-passes #![crate_type="lib"] -#![feature(repr_transparent)] #[repr(C)] pub struct Rgb8 { r: u8, g: u8, b: u8 } diff --git a/src/test/codegen/repr-transparent.rs b/src/test/codegen/repr-transparent.rs index 087fa9b16b4..64a62fd7e88 100644 --- a/src/test/codegen/repr-transparent.rs +++ b/src/test/codegen/repr-transparent.rs @@ -11,7 +11,7 @@ // compile-flags: -C no-prepopulate-passes #![crate_type="lib"] -#![feature(repr_transparent, repr_simd)] +#![feature(repr_simd)] use std::marker::PhantomData; diff --git a/src/test/compile-fail/repr-transparent-other-items.rs b/src/test/compile-fail/repr-transparent-other-items.rs index cf0870866c7..685d62dc3a9 100644 --- a/src/test/compile-fail/repr-transparent-other-items.rs +++ b/src/test/compile-fail/repr-transparent-other-items.rs @@ -8,8 +8,6 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(repr_transparent)] - // See also repr-transparent.rs #[repr(transparent)] //~ ERROR unsupported representation for zero-variant enum diff --git a/src/test/compile-fail/repr-transparent-other-reprs.rs b/src/test/compile-fail/repr-transparent-other-reprs.rs index 7b91a6f68e3..a391c0ae1f8 100644 --- a/src/test/compile-fail/repr-transparent-other-reprs.rs +++ b/src/test/compile-fail/repr-transparent-other-reprs.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -#![feature(repr_transparent, repr_align, attr_literals)] +#![feature(repr_align, attr_literals)] // See also repr-transparent.rs diff --git a/src/test/compile-fail/repr-transparent.rs b/src/test/compile-fail/repr-transparent.rs index b5e6a0fa0b1..4d8ec4cdb40 100644 --- a/src/test/compile-fail/repr-transparent.rs +++ b/src/test/compile-fail/repr-transparent.rs @@ -14,7 +14,6 @@ // - repr-transparent-other-items.rs #![feature(repr_align, attr_literals)] -#![feature(repr_transparent)] use std::marker::PhantomData; diff --git a/src/test/ui/feature-gate-repr_transparent.rs b/src/test/ui/feature-gate-repr_transparent.rs deleted file mode 100644 index deadf2e535d..00000000000 --- a/src/test/ui/feature-gate-repr_transparent.rs +++ /dev/null @@ -1,14 +0,0 @@ -// 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#[repr(transparent)] //~ error: the `#[repr(transparent)]` attribute is experimental -struct Foo(u64); - -fn main() {} diff --git a/src/test/ui/feature-gate-repr_transparent.stderr b/src/test/ui/feature-gate-repr_transparent.stderr deleted file mode 100644 index a4ffaa26690..00000000000 --- a/src/test/ui/feature-gate-repr_transparent.stderr +++ /dev/null @@ -1,11 +0,0 @@ -error[E0658]: the `#[repr(transparent)]` attribute is experimental (see issue #43036) - --> $DIR/feature-gate-repr_transparent.rs:11:1 - | -LL | #[repr(transparent)] //~ error: the `#[repr(transparent)]` attribute is experimental - | ^^^^^^^^^^^^^^^^^^^^ - | - = help: add #![feature(repr_transparent)] to the crate attributes to enable - -error: aborting due to previous error - -For more information about this error, try `rustc --explain E0658`. diff --git a/src/test/ui/lint-ctypes.rs b/src/test/ui/lint-ctypes.rs index 85957831653..4b20001457f 100644 --- a/src/test/ui/lint-ctypes.rs +++ b/src/test/ui/lint-ctypes.rs @@ -9,7 +9,7 @@ // except according to those terms. #![deny(improper_ctypes)] -#![feature(libc, repr_transparent)] +#![feature(libc)] extern crate libc; -- cgit 1.4.1-3-g733a5 From 2177378e34c5ad431b245e3bd7cd6cd38ab9053a Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Wed, 13 Jun 2018 09:09:57 +0200 Subject: Improve core::task::TaskObj --- src/liballoc/boxed.rs | 8 ++++---- src/libcore/task.rs | 29 +++++++++++++++-------------- 2 files changed, 19 insertions(+), 18 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index c794fb8220a..ea60c7775af 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -66,7 +66,7 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll, UnsafePoll, TaskObj}; +use core::task::{Context, Poll, UnsafeTask, TaskObj}; use core::convert::From; use raw_vec::RawVec; @@ -933,7 +933,7 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + Send + 'static> UnsafePoll for PinBox { +unsafe impl + Send + 'static> UnsafeTask for PinBox { fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } @@ -952,13 +952,13 @@ unsafe impl + Send + 'static> UnsafePoll for PinBox { #[unstable(feature = "futures_api", issue = "50547")] impl + Send + 'static> From> for TaskObj { fn from(boxed: PinBox) -> Self { - TaskObj::from_poll_task(boxed) + TaskObj::new(boxed) } } #[unstable(feature = "futures_api", issue = "50547")] impl + Send + 'static> From> for TaskObj { fn from(boxed: Box) -> Self { - TaskObj::from_poll_task(PinBox::from(boxed)) + TaskObj::new(PinBox::from(boxed)) } } diff --git a/src/libcore/task.rs b/src/libcore/task.rs index bef6d3677d0..1a6018ffb65 100644 --- a/src/libcore/task.rs +++ b/src/libcore/task.rs @@ -16,6 +16,8 @@ use fmt; use ptr::NonNull; +use future::Future; +use mem::PinMut; /// Indicates whether a value is available or if the current task has been /// scheduled to receive a wakeup instead. @@ -455,8 +457,8 @@ pub trait Executor { /// `Box + Send>`. pub struct TaskObj { ptr: *mut (), - poll: unsafe fn(*mut (), &mut Context) -> Poll<()>, - drop: unsafe fn(*mut ()), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, + drop_fn: unsafe fn(*mut ()), } impl fmt::Debug for TaskObj { @@ -467,7 +469,6 @@ impl fmt::Debug for TaskObj { } unsafe impl Send for TaskObj {} -unsafe impl Sync for TaskObj {} /// A custom implementation of a task trait object for `TaskObj`, providing /// a hand-rolled vtable. @@ -478,7 +479,7 @@ unsafe impl Sync for TaskObj {} /// The implementor must guarantee that it is safe to call `poll` repeatedly (in /// a non-concurrent fashion) with the result of `into_raw` until `drop` is /// called. -pub unsafe trait UnsafePoll: Send + 'static { +pub unsafe trait UnsafeTask: Send + 'static { /// Convert a owned instance into a (conceptually owned) void pointer. fn into_raw(self) -> *mut (); @@ -504,22 +505,22 @@ pub unsafe trait UnsafePoll: Send + 'static { impl TaskObj { /// Create a `TaskObj` from a custom trait object representation. #[inline] - pub fn from_poll_task(t: T) -> TaskObj { + pub fn new(t: T) -> TaskObj { TaskObj { ptr: t.into_raw(), - poll: T::poll, - drop: T::drop, + poll_fn: T::poll, + drop_fn: T::drop, } } +} + +impl Future for TaskObj { + type Output = (); - /// Poll the task. - /// - /// The semantics here are identical to that for futures, but unlike - /// futures only an `&mut self` reference is needed here. #[inline] - pub fn poll_task(&mut self, cx: &mut Context) -> Poll<()> { + fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { unsafe { - (self.poll)(self.ptr, cx) + (self.poll_fn)(self.ptr, cx) } } } @@ -527,7 +528,7 @@ impl TaskObj { impl Drop for TaskObj { fn drop(&mut self) { unsafe { - (self.drop)(self.ptr) + (self.drop_fn)(self.ptr) } } } -- cgit 1.4.1-3-g733a5 From 2b789bd0570983e82533f9ed30c80312ac334694 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 14 Jun 2018 00:32:30 +0200 Subject: Rename OOM to allocation error The acronym is not descriptive unless one has seen it before. * Rename the `oom` function to `handle_alloc_error`. It was **stabilized in 1.28**, so if we do this at all we need to land it this cycle. * Rename `set_oom_hook` to `set_alloc_error_hook` * Rename `take_oom_hook` to `take_alloc_error_hook` Bikeshed: `alloc` v.s. `allocator`, `error` v.s. `failure` --- src/liballoc/alloc.rs | 14 ++++----- src/liballoc/arc.rs | 4 +-- src/liballoc/raw_vec.rs | 16 +++++++---- src/liballoc/rc.rs | 4 +-- src/libcore/alloc.rs | 48 +++++++++++++++---------------- src/libstd/alloc.rs | 28 +++++++++--------- src/libstd/collections/hash/table.rs | 4 +-- src/test/run-pass/allocator-alloc-one.rs | 6 ++-- src/test/run-pass/realloc-16687.rs | 8 ++++-- src/test/run-pass/regions-mock-codegen.rs | 4 +-- 10 files changed, 72 insertions(+), 64 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/alloc.rs b/src/liballoc/alloc.rs index 04c8063ffeb..84bd275df34 100644 --- a/src/liballoc/alloc.rs +++ b/src/liballoc/alloc.rs @@ -158,7 +158,7 @@ unsafe fn exchange_malloc(size: usize, align: usize) -> *mut u8 { if !ptr.is_null() { ptr } else { - oom(layout) + handle_alloc_error(layout) } } } @@ -184,13 +184,13 @@ pub(crate) unsafe fn box_free(ptr: Unique) { /// /// The default behavior of this function is to print a message to standard error /// and abort the process. -/// It can be replaced with [`set_oom_hook`] and [`take_oom_hook`]. +/// It can be replaced with [`set_alloc_error_hook`] and [`take_alloc_error_hook`]. /// -/// [`set_oom_hook`]: ../../std/alloc/fn.set_oom_hook.html -/// [`take_oom_hook`]: ../../std/alloc/fn.take_oom_hook.html +/// [`set_alloc_error_hook`]: ../../std/alloc/fn.set_alloc_error_hook.html +/// [`take_alloc_error_hook`]: ../../std/alloc/fn.take_alloc_error_hook.html #[stable(feature = "global_alloc", since = "1.28.0")] #[rustc_allocator_nounwind] -pub fn oom(layout: Layout) -> ! { +pub fn handle_alloc_error(layout: Layout) -> ! { #[allow(improper_ctypes)] extern "Rust" { #[lang = "oom"] @@ -204,14 +204,14 @@ mod tests { extern crate test; use self::test::Bencher; use boxed::Box; - use alloc::{Global, Alloc, Layout, oom}; + use alloc::{Global, Alloc, Layout, handle_alloc_error}; #[test] fn allocate_zeroed() { unsafe { let layout = Layout::from_size_align(1024, 1).unwrap(); let ptr = Global.alloc_zeroed(layout.clone()) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); let mut i = ptr.cast::().as_ptr(); let end = i.offset(layout.size() as isize); diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index e3369f0a5b5..0fbd1408f64 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -32,7 +32,7 @@ use core::hash::{Hash, Hasher}; use core::{isize, usize}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use boxed::Box; use string::String; use vec::Vec; @@ -554,7 +554,7 @@ impl Arc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the real ArcInner let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index d1f140e96a3..2369ce648fd 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -14,7 +14,7 @@ use core::ops::Drop; use core::ptr::{self, NonNull, Unique}; use core::slice; -use alloc::{Alloc, Layout, Global, oom}; +use alloc::{Alloc, Layout, Global, handle_alloc_error}; use alloc::CollectionAllocErr; use alloc::CollectionAllocErr::*; use boxed::Box; @@ -104,7 +104,7 @@ impl RawVec { }; match result { Ok(ptr) => ptr.cast(), - Err(_) => oom(layout), + Err(_) => handle_alloc_error(layout), } }; @@ -319,7 +319,9 @@ impl RawVec { new_size); match ptr_res { Ok(ptr) => (new_cap, ptr.cast().into()), - Err(_) => oom(Layout::from_size_align_unchecked(new_size, cur.align())), + Err(_) => handle_alloc_error( + Layout::from_size_align_unchecked(new_size, cur.align()) + ), } } None => { @@ -328,7 +330,7 @@ impl RawVec { let new_cap = if elem_size > (!0) / 8 { 1 } else { 4 }; match self.a.alloc_array::(new_cap) { Ok(ptr) => (new_cap, ptr.into()), - Err(_) => oom(Layout::array::(new_cap).unwrap()), + Err(_) => handle_alloc_error(Layout::array::(new_cap).unwrap()), } } }; @@ -611,7 +613,9 @@ impl RawVec { old_layout, new_size) { Ok(p) => self.ptr = p.cast().into(), - Err(_) => oom(Layout::from_size_align_unchecked(new_size, align)), + Err(_) => handle_alloc_error( + Layout::from_size_align_unchecked(new_size, align) + ), } } self.cap = amount; @@ -673,7 +677,7 @@ impl RawVec { }; match (&res, fallibility) { - (Err(AllocErr), Infallible) => oom(new_layout), + (Err(AllocErr), Infallible) => handle_alloc_error(new_layout), _ => {} } diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 84a6ecf7103..32d624e8fbc 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -259,7 +259,7 @@ use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; -use alloc::{Global, Alloc, Layout, box_free, oom}; +use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use string::String; use vec::Vec; @@ -662,7 +662,7 @@ impl Rc { let layout = Layout::for_value(&*fake_ptr); let mem = Global.alloc(layout) - .unwrap_or_else(|_| oom(layout)); + .unwrap_or_else(|_| handle_alloc_error(layout)); // Initialize the real RcBox let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut RcBox; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 353688d1b85..0c074582281 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -492,10 +492,10 @@ pub unsafe trait GlobalAlloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc(&self, layout: Layout) -> *mut u8; @@ -529,10 +529,10 @@ pub unsafe trait GlobalAlloc { /// just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn alloc_zeroed(&self, layout: Layout) -> *mut u8 { let size = layout.size(); @@ -589,10 +589,10 @@ pub unsafe trait GlobalAlloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html #[stable(feature = "global_alloc", since = "1.28.0")] unsafe fn realloc(&self, ptr: *mut u8, layout: Layout, new_size: usize) -> *mut u8 { let new_layout = Layout::from_size_align_unchecked(new_size, layout.align()); @@ -733,10 +733,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc(&mut self, layout: Layout) -> Result, AllocErr>; /// Deallocate the memory referenced by `ptr`. @@ -843,10 +843,10 @@ pub unsafe trait Alloc { /// library that aborts on memory exhaustion.) /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc(&mut self, ptr: NonNull, layout: Layout, @@ -889,10 +889,10 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc_zeroed(&mut self, layout: Layout) -> Result, AllocErr> { let size = layout.size(); let p = self.alloc(layout); @@ -917,10 +917,10 @@ pub unsafe trait Alloc { /// constraints, just as in `alloc`. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn alloc_excess(&mut self, layout: Layout) -> Result { let usable_size = self.usable_size(&layout); self.alloc(layout).map(|p| Excess(p, usable_size.1)) @@ -941,10 +941,10 @@ pub unsafe trait Alloc { /// constraints, just as in `realloc`. /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc_excess(&mut self, ptr: NonNull, layout: Layout, @@ -986,7 +986,7 @@ pub unsafe trait Alloc { /// unable to assert that the memory block referenced by `ptr` /// could fit `layout`. /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` /// function; clients are expected either to be able to recover from /// `grow_in_place` failures without aborting, or to fall back on /// another reallocation method before resorting to an abort. @@ -1041,7 +1041,7 @@ pub unsafe trait Alloc { /// unable to assert that the memory block referenced by `ptr` /// could fit `layout`. /// - /// Note that one cannot pass `CannotReallocInPlace` to the `oom` + /// Note that one cannot pass `CannotReallocInPlace` to the `handle_alloc_error` /// function; clients are expected either to be able to recover from /// `shrink_in_place` failures without aborting, or to fall back /// on another reallocation method before resorting to an abort. @@ -1090,10 +1090,10 @@ pub unsafe trait Alloc { /// will *not* yield undefined behavior. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html fn alloc_one(&mut self) -> Result, AllocErr> where Self: Sized { @@ -1159,10 +1159,10 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to an - /// allocation error are encouraged to call the [`oom`] function, + /// allocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html fn alloc_array(&mut self, n: usize) -> Result, AllocErr> where Self: Sized { @@ -1206,10 +1206,10 @@ pub unsafe trait Alloc { /// Always returns `Err` on arithmetic overflow. /// /// Clients wishing to abort computation in response to a - /// reallocation error are encouraged to call the [`oom`] function, + /// reallocation error are encouraged to call the [`handle_alloc_error`] function, /// rather than directly invoking `panic!` or similar. /// - /// [`oom`]: ../../alloc/alloc/fn.oom.html + /// [`handle_alloc_error`]: ../../alloc/alloc/fn.handle_alloc_error.html unsafe fn realloc_array(&mut self, ptr: NonNull, n_old: usize, diff --git a/src/libstd/alloc.rs b/src/libstd/alloc.rs index ae74a71dd06..f28e91e19b7 100644 --- a/src/libstd/alloc.rs +++ b/src/libstd/alloc.rs @@ -88,38 +88,38 @@ pub use alloc_system::System; static HOOK: AtomicPtr<()> = AtomicPtr::new(ptr::null_mut()); -/// Registers a custom OOM hook, replacing any that was previously registered. +/// Registers a custom allocation error hook, replacing any that was previously registered. /// -/// The OOM hook is invoked when an infallible memory allocation fails, before +/// The allocation error hook is invoked when an infallible memory allocation fails, before /// the runtime aborts. The default hook prints a message to standard error, -/// but this behavior can be customized with the [`set_oom_hook`] and -/// [`take_oom_hook`] functions. +/// but this behavior can be customized with the [`set_alloc_error_hook`] and +/// [`take_alloc_error_hook`] functions. /// /// The hook is provided with a `Layout` struct which contains information /// about the allocation that failed. /// -/// The OOM hook is a global resource. -#[unstable(feature = "oom_hook", issue = "51245")] -pub fn set_oom_hook(hook: fn(Layout)) { +/// The allocation error hook is a global resource. +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn set_alloc_error_hook(hook: fn(Layout)) { HOOK.store(hook as *mut (), Ordering::SeqCst); } -/// Unregisters the current OOM hook, returning it. +/// Unregisters the current allocation error hook, returning it. /// -/// *See also the function [`set_oom_hook`].* +/// *See also the function [`set_alloc_error_hook`].* /// /// If no custom hook is registered, the default hook will be returned. -#[unstable(feature = "oom_hook", issue = "51245")] -pub fn take_oom_hook() -> fn(Layout) { +#[unstable(feature = "alloc_error_hook", issue = "51245")] +pub fn take_alloc_error_hook() -> fn(Layout) { let hook = HOOK.swap(ptr::null_mut(), Ordering::SeqCst); if hook.is_null() { - default_oom_hook + default_alloc_error_hook } else { unsafe { mem::transmute(hook) } } } -fn default_oom_hook(layout: Layout) { +fn default_alloc_error_hook(layout: Layout) { dumb_print(format_args!("memory allocation of {} bytes failed", layout.size())); } @@ -130,7 +130,7 @@ fn default_oom_hook(layout: Layout) { pub extern fn rust_oom(layout: Layout) -> ! { let hook = HOOK.load(Ordering::SeqCst); let hook: fn(Layout) = if hook.is_null() { - default_oom_hook + default_alloc_error_hook } else { unsafe { mem::transmute(hook) } }; diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index 55f9f4f7cfe..d14b754ddb6 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,7 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, oom}; +use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, handle_alloc_error}; use hash::{BuildHasher, Hash, Hasher}; use marker; use mem::{size_of, needs_drop}; @@ -699,7 +699,7 @@ impl RawTable { // point into it. let (layout, _) = calculate_layout::(capacity)?; let buffer = Global.alloc(layout).map_err(|e| match fallibility { - Infallible => oom(layout), + Infallible => handle_alloc_error(layout), Fallible => e, })?; diff --git a/src/test/run-pass/allocator-alloc-one.rs b/src/test/run-pass/allocator-alloc-one.rs index f1fdbfc702d..f15b013c07b 100644 --- a/src/test/run-pass/allocator-alloc-one.rs +++ b/src/test/run-pass/allocator-alloc-one.rs @@ -10,11 +10,13 @@ #![feature(allocator_api, nonnull)] -use std::alloc::{Alloc, Global, Layout, oom}; +use std::alloc::{Alloc, Global, Layout, handle_alloc_error}; fn main() { unsafe { - let ptr = Global.alloc_one::().unwrap_or_else(|_| oom(Layout::new::())); + let ptr = Global.alloc_one::().unwrap_or_else(|_| { + handle_alloc_error(Layout::new::()) + }); *ptr.as_ptr() = 4; assert_eq!(*ptr.as_ptr(), 4); Global.dealloc_one(ptr); diff --git a/src/test/run-pass/realloc-16687.rs b/src/test/run-pass/realloc-16687.rs index 7152e721eac..3b4b458bb04 100644 --- a/src/test/run-pass/realloc-16687.rs +++ b/src/test/run-pass/realloc-16687.rs @@ -15,7 +15,7 @@ #![feature(heap_api, allocator_api)] -use std::alloc::{Global, Alloc, Layout, oom}; +use std::alloc::{Global, Alloc, Layout, handle_alloc_error}; use std::ptr::{self, NonNull}; fn main() { @@ -50,7 +50,7 @@ unsafe fn test_triangle() -> bool { println!("allocate({:?})", layout); } - let ret = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); + let ret = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); if PRINT { println!("allocate({:?}) = {:?}", layout, ret); @@ -73,7 +73,9 @@ unsafe fn test_triangle() -> bool { } let ret = Global.realloc(NonNull::new_unchecked(ptr), old, new.size()) - .unwrap_or_else(|_| oom(Layout::from_size_align_unchecked(new.size(), old.align()))); + .unwrap_or_else(|_| handle_alloc_error( + Layout::from_size_align_unchecked(new.size(), old.align()) + )); if PRINT { println!("reallocate({:?}, old={:?}, new={:?}) = {:?}", diff --git a/src/test/run-pass/regions-mock-codegen.rs b/src/test/run-pass/regions-mock-codegen.rs index 745a19dec4d..b58e837f3bd 100644 --- a/src/test/run-pass/regions-mock-codegen.rs +++ b/src/test/run-pass/regions-mock-codegen.rs @@ -12,7 +12,7 @@ #![feature(allocator_api)] -use std::alloc::{Alloc, Global, Layout, oom}; +use std::alloc::{Alloc, Global, Layout, handle_alloc_error}; use std::ptr::NonNull; struct arena(()); @@ -33,7 +33,7 @@ struct Ccx { fn alloc<'a>(_bcx : &'a arena) -> &'a Bcx<'a> { unsafe { let layout = Layout::new::(); - let ptr = Global.alloc(layout).unwrap_or_else(|_| oom(layout)); + let ptr = Global.alloc(layout).unwrap_or_else(|_| handle_alloc_error(layout)); &*(ptr.as_ptr() as *const _) } } -- cgit 1.4.1-3-g733a5 From 776544f011a6a5beccb7923a261b0dcecdd2396a Mon Sep 17 00:00:00 2001 From: Esteban Küber Date: Sat, 9 Jun 2018 16:53:36 -0700 Subject: Add message to `rustc_on_unimplemented` attributes in core --- src/liballoc/vec.rs | 10 +- src/libcore/cmp.rs | 10 +- src/libcore/iter/traits.rs | 7 +- src/libcore/marker.rs | 10 +- src/libcore/ops/index.rs | 10 +- src/libstd/panic.rs | 15 ++- src/libsyntax/parse/parser.rs | 8 -- src/test/compile-fail/associated-types-unsized.rs | 2 +- src/test/compile-fail/bad-method-typaram-kind.rs | 2 +- src/test/compile-fail/bad-sized.rs | 4 +- .../builtin-superkinds-double-superkind.rs | 2 +- .../compile-fail/builtin-superkinds-in-metadata.rs | 2 +- src/test/compile-fail/builtin-superkinds-simple.rs | 2 +- .../builtin-superkinds-typaram-not-send.rs | 3 +- ...sure-bounds-cant-promote-superkind-in-struct.rs | 2 +- src/test/compile-fail/dst-bad-assign-2.rs | 2 +- src/test/compile-fail/dst-bad-assign-3.rs | 2 +- src/test/compile-fail/dst-bad-assign.rs | 2 +- src/test/compile-fail/dst-bad-deep-2.rs | 2 +- src/test/compile-fail/dst-bad-deep.rs | 2 +- .../compile-fail/dst-object-from-unsized-type.rs | 8 +- src/test/compile-fail/dst-sized-trait-param.rs | 4 +- .../compile-fail/extern-types-not-sync-send.rs | 2 +- src/test/compile-fail/extern-types-unsized.rs | 8 +- src/test/compile-fail/issue-14366.rs | 2 +- src/test/compile-fail/issue-15756.rs | 2 +- src/test/compile-fail/issue-17651.rs | 2 +- src/test/compile-fail/issue-18107.rs | 2 +- src/test/compile-fail/issue-18919.rs | 2 +- src/test/compile-fail/issue-20005.rs | 2 +- src/test/compile-fail/issue-20433.rs | 2 +- src/test/compile-fail/issue-20605.rs | 2 +- src/test/compile-fail/issue-21763.rs | 2 +- src/test/compile-fail/issue-22874.rs | 2 +- src/test/compile-fail/issue-23281.rs | 2 +- src/test/compile-fail/issue-24446.rs | 2 +- src/test/compile-fail/issue-27060-2.rs | 2 +- src/test/compile-fail/issue-27078.rs | 2 +- src/test/compile-fail/issue-35988.rs | 2 +- src/test/compile-fail/issue-38954.rs | 2 +- src/test/compile-fail/issue-41229-ref-str.rs | 4 +- src/test/compile-fail/issue-42312.rs | 4 +- src/test/compile-fail/issue-5883.rs | 4 +- src/test/compile-fail/issue-7013.rs | 2 +- src/test/compile-fail/kindck-impl-type-params.rs | 8 +- src/test/compile-fail/kindck-nonsendable-1.rs | 2 +- src/test/compile-fail/kindck-send-object.rs | 3 +- src/test/compile-fail/kindck-send-object1.rs | 2 +- src/test/compile-fail/kindck-send-object2.rs | 3 +- src/test/compile-fail/kindck-send-owned.rs | 3 +- src/test/compile-fail/kindck-send-unsafe.rs | 2 +- src/test/compile-fail/no-send-res-ports.rs | 2 +- src/test/compile-fail/no_send-enum.rs | 2 +- src/test/compile-fail/no_send-rc.rs | 2 +- src/test/compile-fail/no_send-struct.rs | 2 +- src/test/compile-fail/not-panic-safe-2.rs | 4 +- src/test/compile-fail/not-panic-safe-3.rs | 4 +- src/test/compile-fail/not-panic-safe-4.rs | 4 +- src/test/compile-fail/not-panic-safe-6.rs | 4 +- src/test/compile-fail/not-panic-safe.rs | 3 +- src/test/compile-fail/range-1.rs | 2 +- src/test/compile-fail/range_traits-1.rs | 24 ++--- src/test/compile-fail/str-idx.rs | 2 +- src/test/compile-fail/str-mut-idx.rs | 6 +- src/test/compile-fail/substs-ppaux.rs | 4 +- .../compile-fail/trait-bounds-not-on-bare-trait.rs | 2 +- src/test/compile-fail/traits-negative-impls.rs | 14 +-- .../typeck-default-trait-impl-negation-send.rs | 2 +- src/test/compile-fail/union/union-unsized.rs | 6 +- src/test/compile-fail/unsized-bare-typaram.rs | 3 +- src/test/compile-fail/unsized-enum.rs | 2 +- .../unsized-inherent-impl-self-type.rs | 3 +- src/test/compile-fail/unsized-struct.rs | 4 +- .../compile-fail/unsized-trait-impl-self-type.rs | 3 +- .../compile-fail/unsized-trait-impl-trait-arg.rs | 2 +- src/test/compile-fail/unsized3.rs | 12 +-- src/test/compile-fail/unsized5.rs | 18 ++-- src/test/compile-fail/unsized6.rs | 39 ++++--- src/test/compile-fail/unsized7.rs | 2 +- src/test/ui/const-unsized.rs | 8 +- src/test/ui/const-unsized.stderr | 8 +- src/test/ui/error-codes/E0277-2.rs | 2 +- src/test/ui/error-codes/E0277-2.stderr | 2 +- src/test/ui/error-codes/E0277.rs | 2 +- src/test/ui/error-codes/E0277.stderr | 2 +- src/test/ui/feature-gate-trivial_bounds.stderr | 6 +- src/test/ui/generator/sized-yield.rs | 6 +- src/test/ui/generator/sized-yield.stderr | 11 +- src/test/ui/impl-trait/auto-trait-leak.rs | 2 +- src/test/ui/impl-trait/auto-trait-leak.stderr | 2 +- src/test/ui/impl-trait/auto-trait-leak2.rs | 4 +- src/test/ui/impl-trait/auto-trait-leak2.stderr | 4 +- .../ui/interior-mutability/interior-mutability.rs | 3 +- .../interior-mutability/interior-mutability.stderr | 6 +- src/test/ui/mismatched_types/binops.rs | 4 +- src/test/ui/mismatched_types/binops.stderr | 12 +-- src/test/ui/mismatched_types/cast-rfc0401.rs | 4 +- src/test/ui/mismatched_types/cast-rfc0401.stderr | 8 +- src/test/ui/partialeq_help.stderr | 4 +- src/test/ui/resolve/issue-5035-2.rs | 3 +- src/test/ui/resolve/issue-5035-2.stderr | 4 +- src/test/ui/suggestions/str-array-assignment.rs | 2 +- .../ui/suggestions/str-array-assignment.stderr | 2 +- src/test/ui/trait-suggest-where-clause.rs | 8 +- src/test/ui/trait-suggest-where-clause.stderr | 8 +- src/test/ui/trivial-bounds-leak.stderr | 2 +- src/test/ui/type-check-defaults.rs | 14 +-- src/test/ui/type-check-defaults.stderr | 8 +- src/test/ui/union/union-sized-field.rs | 9 +- src/test/ui/union/union-sized-field.stderr | 16 +-- src/test/ui/unsized-enum2.rs | 57 +++++++---- src/test/ui/unsized-enum2.stderr | 112 ++++++++++----------- 112 files changed, 394 insertions(+), 318 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index b5739e1a825..752a6c966d5 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -1693,7 +1693,10 @@ impl Hash for Vec { } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] +#[rustc_on_unimplemented( + message="vector indices are of type `usize` or ranges of `usize`", + label="vector indices are of type `usize` or ranges of `usize`", +)] impl Index for Vec where I: ::core::slice::SliceIndex<[T]>, @@ -1707,7 +1710,10 @@ where } #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "vector indices are of type `usize` or ranges of `usize`"] +#[rustc_on_unimplemented( + message="vector indices are of type `usize` or ranges of `usize`", + label="vector indices are of type `usize` or ranges of `usize`", +)] impl IndexMut for Vec where I: ::core::slice::SliceIndex<[T]>, diff --git a/src/libcore/cmp.rs b/src/libcore/cmp.rs index 13e838773a5..3626a266ad5 100644 --- a/src/libcore/cmp.rs +++ b/src/libcore/cmp.rs @@ -108,7 +108,10 @@ use self::Ordering::*; #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "==")] #[doc(alias = "!=")] -#[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] +#[rustc_on_unimplemented( + message="can't compare `{Self}` with `{Rhs}`", + label="no implementation for `{Self} == {Rhs}`", +)] pub trait PartialEq { /// This method tests for `self` and `other` values to be equal, and is used /// by `==`. @@ -611,7 +614,10 @@ impl PartialOrd for Ordering { #[doc(alias = "<")] #[doc(alias = "<=")] #[doc(alias = ">=")] -#[rustc_on_unimplemented = "can't compare `{Self}` with `{Rhs}`"] +#[rustc_on_unimplemented( + message="can't compare `{Self}` with `{Rhs}`", + label="no implementation for `{Self} < {Rhs}` and `{Self} > {Rhs}`", +)] pub trait PartialOrd: PartialEq { /// This method returns an ordering between `self` and `other` values if one exists. /// diff --git a/src/libcore/iter/traits.rs b/src/libcore/iter/traits.rs index 3d2ce9e6b10..4b2c1aa551e 100644 --- a/src/libcore/iter/traits.rs +++ b/src/libcore/iter/traits.rs @@ -104,8 +104,11 @@ use super::LoopState; /// assert_eq!(c.0, vec![0, 1, 2, 3, 4]); /// ``` #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented="a collection of type `{Self}` cannot be \ - built from an iterator over elements of type `{A}`"] +#[rustc_on_unimplemented( + message="a collection of type `{Self}` cannot be built from an iterator \ + over elements of type `{A}`", + label="a collection of type `{Self}` cannot be built from `std::iter::Iterator`", +)] pub trait FromIterator: Sized { /// Creates a value from an iterator. /// diff --git a/src/libcore/marker.rs b/src/libcore/marker.rs index 3d3f63ecf37..d5416e393f4 100644 --- a/src/libcore/marker.rs +++ b/src/libcore/marker.rs @@ -39,7 +39,10 @@ use hash::Hasher; /// [arc]: ../../std/sync/struct.Arc.html /// [ub]: ../../reference/behavior-considered-undefined.html #[stable(feature = "rust1", since = "1.0.0")] -#[rustc_on_unimplemented = "`{Self}` cannot be sent between threads safely"] +#[rustc_on_unimplemented( + message="`{Self}` cannot be sent between threads safely", + label="`{Self}` cannot be sent between threads safely" +)] pub unsafe auto trait Send { // empty. } @@ -88,7 +91,10 @@ impl !Send for *mut T { } /// [trait object]: ../../book/first-edition/trait-objects.html #[stable(feature = "rust1", since = "1.0.0")] #[lang = "sized"] -#[rustc_on_unimplemented = "`{Self}` does not have a constant size known at compile-time"] +#[rustc_on_unimplemented( + message="`{Self}` does not have a constant size known at compile-time", + label="`{Self}` does not have a constant size known at compile-time" +)] #[fundamental] // for Default, for example, which requires that `[T]: !Default` be evaluatable pub trait Sized { // Empty. diff --git a/src/libcore/ops/index.rs b/src/libcore/ops/index.rs index 0a0e92a9180..1ac80ecc96f 100644 --- a/src/libcore/ops/index.rs +++ b/src/libcore/ops/index.rs @@ -60,7 +60,10 @@ /// assert_eq!(nucleotide_count[Nucleotide::T], 12); /// ``` #[lang = "index"] -#[rustc_on_unimplemented = "the type `{Self}` cannot be indexed by `{Idx}`"] +#[rustc_on_unimplemented( + message="the type `{Self}` cannot be indexed by `{Idx}`", + label="`{Self}` cannot be indexed by `{Idx}`", +)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "]")] #[doc(alias = "[")] @@ -147,7 +150,10 @@ pub trait Index { /// balance[Side::Left] = Weight::Kilogram(3.0); /// ``` #[lang = "index_mut"] -#[rustc_on_unimplemented = "the type `{Self}` cannot be mutably indexed by `{Idx}`"] +#[rustc_on_unimplemented( + message="the type `{Self}` cannot be mutably indexed by `{Idx}`", + label="`{Self}` cannot be mutably indexed by `{Idx}`", +)] #[stable(feature = "rust1", since = "1.0.0")] #[doc(alias = "[")] #[doc(alias = "]")] diff --git a/src/libstd/panic.rs b/src/libstd/panic.rs index 4b5a063ea73..2c11c262488 100644 --- a/src/libstd/panic.rs +++ b/src/libstd/panic.rs @@ -110,8 +110,10 @@ pub use core::panic::{PanicInfo, Location}; /// /// [`AssertUnwindSafe`]: ./struct.AssertUnwindSafe.html #[stable(feature = "catch_unwind", since = "1.9.0")] -#[rustc_on_unimplemented = "the type {Self} may not be safely transferred \ - across an unwind boundary"] +#[rustc_on_unimplemented( + message="the type `{Self}` may not be safely transferred across an unwind boundary", + label="`{Self}` may not be safely transferred across an unwind boundary", +)] pub auto trait UnwindSafe {} /// A marker trait representing types where a shared reference is considered @@ -126,9 +128,12 @@ pub auto trait UnwindSafe {} /// [`UnsafeCell`]: ../cell/struct.UnsafeCell.html /// [`UnwindSafe`]: ./trait.UnwindSafe.html #[stable(feature = "catch_unwind", since = "1.9.0")] -#[rustc_on_unimplemented = "the type {Self} may contain interior mutability \ - and a reference may not be safely transferrable \ - across a catch_unwind boundary"] +#[rustc_on_unimplemented( + message="the type `{Self}` may contain interior mutability and a reference may not be safely \ + transferrable across a catch_unwind boundary", + label="`{Self}` may contain interior mutability and a reference may not be safely \ + transferrable across a catch_unwind boundary", +)] pub auto trait RefUnwindSafe {} /// A simple wrapper around a type to assert that it is unwind safe. diff --git a/src/libsyntax/parse/parser.rs b/src/libsyntax/parse/parser.rs index afb0931f950..5d04aa711c1 100644 --- a/src/libsyntax/parse/parser.rs +++ b/src/libsyntax/parse/parser.rs @@ -1204,14 +1204,6 @@ impl<'a> Parser<'a> { fn span_fatal_err>(&self, sp: S, err: Error) -> DiagnosticBuilder<'a> { err.span_err(sp, self.diagnostic()) } - fn span_fatal_help>(&self, - sp: S, - m: &str, - help: &str) -> DiagnosticBuilder<'a> { - let mut err = self.sess.span_diagnostic.struct_span_fatal(sp, m); - err.help(help); - err - } fn bug(&self, m: &str) -> ! { self.sess.span_diagnostic.span_bug(self.span, m) } diff --git a/src/test/compile-fail/associated-types-unsized.rs b/src/test/compile-fail/associated-types-unsized.rs index f1827022964..8c0ce26b294 100644 --- a/src/test/compile-fail/associated-types-unsized.rs +++ b/src/test/compile-fail/associated-types-unsized.rs @@ -14,7 +14,7 @@ trait Get { } fn foo(t: T) { - let x = t.get(); //~ ERROR `::Value: std::marker::Sized` is not + let x = t.get(); //~ ERROR `::Value` does not have a constant size known at compile-time } fn main() { diff --git a/src/test/compile-fail/bad-method-typaram-kind.rs b/src/test/compile-fail/bad-method-typaram-kind.rs index 5be90f05018..7cef3f13dfc 100644 --- a/src/test/compile-fail/bad-method-typaram-kind.rs +++ b/src/test/compile-fail/bad-method-typaram-kind.rs @@ -9,7 +9,7 @@ // except according to those terms. fn foo() { - 1.bar::(); //~ ERROR `T: std::marker::Send` is not satisfied + 1.bar::(); //~ ERROR `T` cannot be sent between threads safely } trait bar { diff --git a/src/test/compile-fail/bad-sized.rs b/src/test/compile-fail/bad-sized.rs index df3da5096bf..55b009aef4f 100644 --- a/src/test/compile-fail/bad-sized.rs +++ b/src/test/compile-fail/bad-sized.rs @@ -13,6 +13,6 @@ trait Trait {} pub fn main() { let x: Vec = Vec::new(); //~^ ERROR only auto traits can be used as additional traits in a trait object - //~| ERROR the trait bound `Trait: std::marker::Sized` is not satisfied - //~| ERROR the trait bound `Trait: std::marker::Sized` is not satisfied + //~| ERROR `Trait` does not have a constant size known at compile-time + //~| ERROR `Trait` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/builtin-superkinds-double-superkind.rs b/src/test/compile-fail/builtin-superkinds-double-superkind.rs index 261881d880b..3f7f2adabdf 100644 --- a/src/test/compile-fail/builtin-superkinds-double-superkind.rs +++ b/src/test/compile-fail/builtin-superkinds-double-superkind.rs @@ -14,7 +14,7 @@ trait Foo : Send+Sync { } impl Foo for (T,) { } -//~^ ERROR the trait bound `T: std::marker::Send` is not satisfied in `(T,)` [E0277] +//~^ ERROR `T` cannot be sent between threads safely [E0277] impl Foo for (T,T) { } //~^ ERROR `T` cannot be shared between threads safely [E0277] diff --git a/src/test/compile-fail/builtin-superkinds-in-metadata.rs b/src/test/compile-fail/builtin-superkinds-in-metadata.rs index de2084c4e81..88b5a3fbb55 100644 --- a/src/test/compile-fail/builtin-superkinds-in-metadata.rs +++ b/src/test/compile-fail/builtin-superkinds-in-metadata.rs @@ -22,6 +22,6 @@ struct X(T); impl RequiresShare for X { } impl RequiresRequiresShareAndSend for X { } -//~^ ERROR `T: std::marker::Send` is not satisfied +//~^ ERROR `T` cannot be sent between threads safely [E0277] fn main() { } diff --git a/src/test/compile-fail/builtin-superkinds-simple.rs b/src/test/compile-fail/builtin-superkinds-simple.rs index 6dc5f39cb30..22dc9598d29 100644 --- a/src/test/compile-fail/builtin-superkinds-simple.rs +++ b/src/test/compile-fail/builtin-superkinds-simple.rs @@ -14,6 +14,6 @@ trait Foo : Send { } impl Foo for std::rc::Rc { } -//~^ ERROR `std::rc::Rc: std::marker::Send` is not satisfied +//~^ ERROR `std::rc::Rc` cannot be sent between threads safely fn main() { } diff --git a/src/test/compile-fail/builtin-superkinds-typaram-not-send.rs b/src/test/compile-fail/builtin-superkinds-typaram-not-send.rs index d4bb8de13d0..e0b2043c110 100644 --- a/src/test/compile-fail/builtin-superkinds-typaram-not-send.rs +++ b/src/test/compile-fail/builtin-superkinds-typaram-not-send.rs @@ -12,6 +12,7 @@ trait Foo : Send { } -impl Foo for T { } //~ ERROR `T: std::marker::Send` is not satisfied +impl Foo for T { } +//~^ ERROR `T` cannot be sent between threads safely fn main() { } diff --git a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs index b9224e7be7f..12e9fb30902 100644 --- a/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs +++ b/src/test/compile-fail/closure-bounds-cant-promote-superkind-in-struct.rs @@ -13,7 +13,7 @@ struct X where F: FnOnce() + 'static + Send { } fn foo(blk: F) -> X where F: FnOnce() + 'static { - //~^ ERROR `F: std::marker::Send` is not satisfied + //~^ ERROR `F` cannot be sent between threads safely return X { field: blk }; } diff --git a/src/test/compile-fail/dst-bad-assign-2.rs b/src/test/compile-fail/dst-bad-assign-2.rs index 10c8f1eed00..c3cbc904842 100644 --- a/src/test/compile-fail/dst-bad-assign-2.rs +++ b/src/test/compile-fail/dst-bad-assign-2.rs @@ -43,5 +43,5 @@ pub fn main() { let f5: &mut Fat = &mut Fat { f1: 5, f2: "some str", ptr: Bar1 {f :42} }; let z: Box = Box::new(Bar1 {f: 36}); f5.ptr = *z; - //~^ ERROR `ToBar: std::marker::Sized` is not satisfied + //~^ ERROR `ToBar` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/dst-bad-assign-3.rs b/src/test/compile-fail/dst-bad-assign-3.rs index ceaa3716232..1cd5b51fe34 100644 --- a/src/test/compile-fail/dst-bad-assign-3.rs +++ b/src/test/compile-fail/dst-bad-assign-3.rs @@ -45,5 +45,5 @@ pub fn main() { //~| expected type `ToBar` //~| found type `Bar1` //~| expected trait ToBar, found struct `Bar1` - //~| ERROR `ToBar: std::marker::Sized` is not satisfied + //~| ERROR `ToBar` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/dst-bad-assign.rs b/src/test/compile-fail/dst-bad-assign.rs index 4f7d07600ad..dcd78ac044c 100644 --- a/src/test/compile-fail/dst-bad-assign.rs +++ b/src/test/compile-fail/dst-bad-assign.rs @@ -47,5 +47,5 @@ pub fn main() { //~| expected type `ToBar` //~| found type `Bar1` //~| expected trait ToBar, found struct `Bar1` - //~| ERROR `ToBar: std::marker::Sized` is not satisfied + //~| ERROR `ToBar` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/dst-bad-deep-2.rs b/src/test/compile-fail/dst-bad-deep-2.rs index 0c812b1d815..9ed2ec8d98d 100644 --- a/src/test/compile-fail/dst-bad-deep-2.rs +++ b/src/test/compile-fail/dst-bad-deep-2.rs @@ -19,5 +19,5 @@ pub fn main() { let f: ([isize; 3],) = ([5, 6, 7],); let g: &([isize],) = &f; let h: &(([isize],),) = &(*g,); - //~^ ERROR `[isize]: std::marker::Sized` is not satisfied + //~^ ERROR `[isize]` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/dst-bad-deep.rs b/src/test/compile-fail/dst-bad-deep.rs index f508364d751..9b575ae4aad 100644 --- a/src/test/compile-fail/dst-bad-deep.rs +++ b/src/test/compile-fail/dst-bad-deep.rs @@ -21,5 +21,5 @@ pub fn main() { let f: Fat<[isize; 3]> = Fat { ptr: [5, 6, 7] }; let g: &Fat<[isize]> = &f; let h: &Fat> = &Fat { ptr: *g }; - //~^ ERROR `[isize]: std::marker::Sized` is not satisfied + //~^ ERROR `[isize]` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/dst-object-from-unsized-type.rs b/src/test/compile-fail/dst-object-from-unsized-type.rs index 8fafd78d407..678eef76fde 100644 --- a/src/test/compile-fail/dst-object-from-unsized-type.rs +++ b/src/test/compile-fail/dst-object-from-unsized-type.rs @@ -16,22 +16,22 @@ impl Foo for [u8] {} fn test1(t: &T) { let u: &Foo = t; - //~^ ERROR `T: std::marker::Sized` is not satisfied + //~^ ERROR `T` does not have a constant size known at compile-time } fn test2(t: &T) { let v: &Foo = t as &Foo; - //~^ ERROR `T: std::marker::Sized` is not satisfied + //~^ ERROR `T` does not have a constant size known at compile-time } fn test3() { let _: &[&Foo] = &["hi"]; - //~^ ERROR `str: std::marker::Sized` is not satisfied + //~^ ERROR `str` does not have a constant size known at compile-time } fn test4(x: &[u8]) { let _: &Foo = x as &Foo; - //~^ ERROR `[u8]: std::marker::Sized` is not satisfied + //~^ ERROR `[u8]` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/compile-fail/dst-sized-trait-param.rs b/src/test/compile-fail/dst-sized-trait-param.rs index bd5fd3ee3b7..cafd67809f8 100644 --- a/src/test/compile-fail/dst-sized-trait-param.rs +++ b/src/test/compile-fail/dst-sized-trait-param.rs @@ -15,9 +15,9 @@ trait Foo : Sized { fn take(self, x: &T) { } } // Note: T is sized impl Foo<[isize]> for usize { } -//~^ ERROR `[isize]: std::marker::Sized` is not satisfied +//~^ ERROR `[isize]` does not have a constant size known at compile-time impl Foo for [usize] { } -//~^ ERROR `[usize]: std::marker::Sized` is not satisfied +//~^ ERROR `[usize]` does not have a constant size known at compile-time pub fn main() { } diff --git a/src/test/compile-fail/extern-types-not-sync-send.rs b/src/test/compile-fail/extern-types-not-sync-send.rs index 6a7a515ba5f..10abb80a2f7 100644 --- a/src/test/compile-fail/extern-types-not-sync-send.rs +++ b/src/test/compile-fail/extern-types-not-sync-send.rs @@ -24,5 +24,5 @@ fn main() { //~^ ERROR `A` cannot be shared between threads safely [E0277] assert_send::(); - //~^ ERROR the trait bound `A: std::marker::Send` is not satisfied + //~^ ERROR `A` cannot be sent between threads safely [E0277] } diff --git a/src/test/compile-fail/extern-types-unsized.rs b/src/test/compile-fail/extern-types-unsized.rs index faa27894806..b3e19899a67 100644 --- a/src/test/compile-fail/extern-types-unsized.rs +++ b/src/test/compile-fail/extern-types-unsized.rs @@ -30,14 +30,14 @@ fn assert_sized() { } fn main() { assert_sized::(); - //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + //~^ ERROR `A` does not have a constant size known at compile-time assert_sized::(); - //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + //~^ ERROR `A` does not have a constant size known at compile-time assert_sized::>(); - //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + //~^ ERROR `A` does not have a constant size known at compile-time assert_sized::>>(); - //~^ ERROR the trait bound `A: std::marker::Sized` is not satisfied + //~^ ERROR `A` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/issue-14366.rs b/src/test/compile-fail/issue-14366.rs index 84452accc9a..7b4954a2d4e 100644 --- a/src/test/compile-fail/issue-14366.rs +++ b/src/test/compile-fail/issue-14366.rs @@ -10,5 +10,5 @@ fn main() { let _x = "test" as &::std::any::Any; -//~^ ERROR `str: std::marker::Sized` is not satisfied + //~^ ERROR `str` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/issue-15756.rs b/src/test/compile-fail/issue-15756.rs index 41349d7d744..0df82de8338 100644 --- a/src/test/compile-fail/issue-15756.rs +++ b/src/test/compile-fail/issue-15756.rs @@ -15,7 +15,7 @@ fn dft_iter<'a, T>(arg1: Chunks<'a,T>, arg2: ChunksMut<'a,T>) { for &mut something -//~^ ERROR `[T]: std::marker::Sized` is not satisfied + //~^ ERROR `[T]` does not have a constant size known at compile-time in arg2 { } diff --git a/src/test/compile-fail/issue-17651.rs b/src/test/compile-fail/issue-17651.rs index 4996da057dd..13548b06ea1 100644 --- a/src/test/compile-fail/issue-17651.rs +++ b/src/test/compile-fail/issue-17651.rs @@ -13,5 +13,5 @@ fn main() { (|| Box::new(*(&[0][..])))(); - //~^ ERROR `[{integer}]: std::marker::Sized` is not satisfied + //~^ ERROR `[{integer}]` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/issue-18107.rs b/src/test/compile-fail/issue-18107.rs index 33d68c121bf..5faa8885e73 100644 --- a/src/test/compile-fail/issue-18107.rs +++ b/src/test/compile-fail/issue-18107.rs @@ -12,7 +12,7 @@ pub trait AbstractRenderer {} fn _create_render(_: &()) -> AbstractRenderer -//~^ ERROR: `AbstractRenderer + 'static: std::marker::Sized` is not satisfied +//~^ ERROR: `AbstractRenderer + 'static` does not have a constant size known at compile-time { match 0 { _ => unimplemented!() diff --git a/src/test/compile-fail/issue-18919.rs b/src/test/compile-fail/issue-18919.rs index 3e21360721b..14b776cb1ff 100644 --- a/src/test/compile-fail/issue-18919.rs +++ b/src/test/compile-fail/issue-18919.rs @@ -11,7 +11,7 @@ type FuncType<'f> = Fn(&isize) -> isize + 'f; fn ho_func(f: Option) { - //~^ ERROR: `for<'r> std::ops::Fn(&'r isize) -> isize: std::marker::Sized` is not satisfied + //~^ ERROR: `for<'r> std::ops::Fn(&'r isize) -> isize` does not have a constant size known at } fn main() {} diff --git a/src/test/compile-fail/issue-20005.rs b/src/test/compile-fail/issue-20005.rs index b02757fb5a3..ab47f687fc2 100644 --- a/src/test/compile-fail/issue-20005.rs +++ b/src/test/compile-fail/issue-20005.rs @@ -15,7 +15,7 @@ trait From { } trait To { - fn to( //~ ERROR `Self: std::marker::Sized` is not satisfied + fn to( //~ ERROR `Self` does not have a constant size known at compile-time self ) -> >::Result where Dst: From { From::from(self) diff --git a/src/test/compile-fail/issue-20433.rs b/src/test/compile-fail/issue-20433.rs index d1a139e698e..d8ca00d313f 100644 --- a/src/test/compile-fail/issue-20433.rs +++ b/src/test/compile-fail/issue-20433.rs @@ -14,5 +14,5 @@ struct The; impl The { fn iceman(c: Vec<[i32]>) {} - //~^ ERROR the trait bound `[i32]: std::marker::Sized` is not satisfied + //~^ ERROR `[i32]` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/issue-20605.rs b/src/test/compile-fail/issue-20605.rs index 5eb0e4360fc..c5a724bba80 100644 --- a/src/test/compile-fail/issue-20605.rs +++ b/src/test/compile-fail/issue-20605.rs @@ -10,7 +10,7 @@ fn changer<'a>(mut things: Box>) { for item in *things { *item = 0 } -//~^ ERROR the trait bound `std::iter::Iterator: std::marker::Sized` is not satisfied +//~^ ERROR `std::iter::Iterator` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/compile-fail/issue-21763.rs b/src/test/compile-fail/issue-21763.rs index cb0baee0a87..b4f952c87d4 100644 --- a/src/test/compile-fail/issue-21763.rs +++ b/src/test/compile-fail/issue-21763.rs @@ -17,5 +17,5 @@ fn foo() {} fn main() { foo::, Rc<()>>>(); - //~^ ERROR: `std::rc::Rc<()>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely } diff --git a/src/test/compile-fail/issue-22874.rs b/src/test/compile-fail/issue-22874.rs index 0df84a436c0..de176486af7 100644 --- a/src/test/compile-fail/issue-22874.rs +++ b/src/test/compile-fail/issue-22874.rs @@ -10,7 +10,7 @@ struct Table { rows: [[String]], - //~^ ERROR the trait bound `[std::string::String]: std::marker::Sized` is not satisfied [E0277] + //~^ ERROR `[std::string::String]` does not have a constant size known at compile-time } fn f(table: &Table) -> &[String] { diff --git a/src/test/compile-fail/issue-23281.rs b/src/test/compile-fail/issue-23281.rs index 5feeb36b1e4..fdab3b59d1b 100644 --- a/src/test/compile-fail/issue-23281.rs +++ b/src/test/compile-fail/issue-23281.rs @@ -14,7 +14,7 @@ pub struct Struct; impl Struct { pub fn function(funs: Vec ()>) {} - //~^ ERROR the trait bound `std::ops::Fn() + 'static: std::marker::Sized` is not satisfied + //~^ ERROR `std::ops::Fn() + 'static` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/compile-fail/issue-24446.rs b/src/test/compile-fail/issue-24446.rs index acd50bcf9e1..5f36bbcf5fd 100644 --- a/src/test/compile-fail/issue-24446.rs +++ b/src/test/compile-fail/issue-24446.rs @@ -11,7 +11,7 @@ fn main() { static foo: Fn() -> u32 = || -> u32 { //~^ ERROR: mismatched types - //~| ERROR: `std::ops::Fn() -> u32 + 'static: std::marker::Sized` is not satisfied + //~| ERROR: `std::ops::Fn() -> u32 + 'static` does not have a constant size known at compile-time 0 }; } diff --git a/src/test/compile-fail/issue-27060-2.rs b/src/test/compile-fail/issue-27060-2.rs index 28180b05c8d..123bbf3358d 100644 --- a/src/test/compile-fail/issue-27060-2.rs +++ b/src/test/compile-fail/issue-27060-2.rs @@ -10,7 +10,7 @@ #[repr(packed)] pub struct Bad { - data: T, //~ ERROR `T: std::marker::Sized` is not satisfied + data: T, //~ ERROR `T` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/compile-fail/issue-27078.rs b/src/test/compile-fail/issue-27078.rs index f34bef62249..32933fa6176 100644 --- a/src/test/compile-fail/issue-27078.rs +++ b/src/test/compile-fail/issue-27078.rs @@ -13,7 +13,7 @@ trait Foo { const BAR: i32; fn foo(self) -> &'static i32 { - //~^ ERROR the trait bound `Self: std::marker::Sized` is not satisfied + //~^ ERROR `Self` does not have a constant size known at compile-time &::BAR } } diff --git a/src/test/compile-fail/issue-35988.rs b/src/test/compile-fail/issue-35988.rs index c2e6a88a57b..8f5b68986e5 100644 --- a/src/test/compile-fail/issue-35988.rs +++ b/src/test/compile-fail/issue-35988.rs @@ -10,7 +10,7 @@ enum E { V([Box]), - //~^ ERROR the trait bound `[std::boxed::Box]: std::marker::Sized` is not satisfied [E0277] + //~^ ERROR `[std::boxed::Box]` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/compile-fail/issue-38954.rs b/src/test/compile-fail/issue-38954.rs index 896728b6da0..960099f3193 100644 --- a/src/test/compile-fail/issue-38954.rs +++ b/src/test/compile-fail/issue-38954.rs @@ -9,6 +9,6 @@ // except according to those terms. fn _test(ref _p: str) {} -//~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied [E0277] +//~^ ERROR `str` does not have a constant size known at compile-time fn main() { } diff --git a/src/test/compile-fail/issue-41229-ref-str.rs b/src/test/compile-fail/issue-41229-ref-str.rs index 31bc21c23ba..b1e24c818d8 100644 --- a/src/test/compile-fail/issue-41229-ref-str.rs +++ b/src/test/compile-fail/issue-41229-ref-str.rs @@ -9,8 +9,6 @@ // except according to those terms. pub fn example(ref s: str) {} -//~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied -//~| `str` does not have a constant size known at compile-time -//~| the trait `std::marker::Sized` is not implemented for `str` +//~^ ERROR `str` does not have a constant size known at compile-time fn main() {} diff --git a/src/test/compile-fail/issue-42312.rs b/src/test/compile-fail/issue-42312.rs index 06573b42b59..89eb5c5ebc1 100644 --- a/src/test/compile-fail/issue-42312.rs +++ b/src/test/compile-fail/issue-42312.rs @@ -12,10 +12,10 @@ use std::ops::Deref; pub trait Foo { fn baz(_: Self::Target) where Self: Deref {} - //~^ ERROR `::Target: std::marker::Sized` is not satisfied + //~^ ERROR `::Target` does not have a constant size known at } pub fn f(_: ToString) {} -//~^ ERROR the trait bound `std::string::ToString + 'static: std::marker::Sized` is not satisfied +//~^ ERROR `std::string::ToString + 'static` does not have a constant size known at compile-time fn main() { } diff --git a/src/test/compile-fail/issue-5883.rs b/src/test/compile-fail/issue-5883.rs index e14d9f3a35c..580625f4955 100644 --- a/src/test/compile-fail/issue-5883.rs +++ b/src/test/compile-fail/issue-5883.rs @@ -15,8 +15,8 @@ struct Struct { } fn new_struct(r: A+'static) - -> Struct { //~^ ERROR `A + 'static: std::marker::Sized` is not satisfied - //~^ ERROR `A + 'static: std::marker::Sized` is not satisfied + -> Struct { //~^ ERROR `A + 'static` does not have a constant size known at compile-time + //~^ ERROR `A + 'static` does not have a constant size known at compile-time Struct { r: r } } diff --git a/src/test/compile-fail/issue-7013.rs b/src/test/compile-fail/issue-7013.rs index 95bbd4eccf4..0c19780bcb4 100644 --- a/src/test/compile-fail/issue-7013.rs +++ b/src/test/compile-fail/issue-7013.rs @@ -34,5 +34,5 @@ struct A { fn main() { let a = A {v: box B{v: None} as Box}; - //~^ ERROR `std::rc::Rc>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely } diff --git a/src/test/compile-fail/kindck-impl-type-params.rs b/src/test/compile-fail/kindck-impl-type-params.rs index 2a86cdef981..3a0e66f58e0 100644 --- a/src/test/compile-fail/kindck-impl-type-params.rs +++ b/src/test/compile-fail/kindck-impl-type-params.rs @@ -26,15 +26,15 @@ impl Gettable for S {} fn f(val: T) { let t: S = S(marker::PhantomData); let a = &t as &Gettable; - //~^ ERROR : std::marker::Send` is not satisfied - //~^^ ERROR : std::marker::Copy` is not satisfied + //~^ ERROR `T` cannot be sent between threads safely + //~| ERROR : std::marker::Copy` is not satisfied } fn g(val: T) { let t: S = S(marker::PhantomData); let a: &Gettable = &t; - //~^ ERROR : std::marker::Send` is not satisfied - //~^^ ERROR : std::marker::Copy` is not satisfied + //~^ ERROR `T` cannot be sent between threads safely + //~| ERROR : std::marker::Copy` is not satisfied } fn foo<'a>() { diff --git a/src/test/compile-fail/kindck-nonsendable-1.rs b/src/test/compile-fail/kindck-nonsendable-1.rs index dd77c2c138f..43c212b2af5 100644 --- a/src/test/compile-fail/kindck-nonsendable-1.rs +++ b/src/test/compile-fail/kindck-nonsendable-1.rs @@ -18,5 +18,5 @@ fn bar(_: F) { } fn main() { let x = Rc::new(3); bar(move|| foo(x)); - //~^ ERROR : std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc` cannot be sent between threads safely } diff --git a/src/test/compile-fail/kindck-send-object.rs b/src/test/compile-fail/kindck-send-object.rs index a84eae0bfda..a3eb47be3ee 100644 --- a/src/test/compile-fail/kindck-send-object.rs +++ b/src/test/compile-fail/kindck-send-object.rs @@ -24,7 +24,8 @@ fn object_ref_with_static_bound_not_ok() { } fn box_object_with_no_bound_not_ok<'a>() { - assert_send::>(); //~ ERROR : std::marker::Send` is not satisfied + assert_send::>(); + //~^ ERROR `Dummy` cannot be sent between threads safely } fn object_with_send_bound_ok() { diff --git a/src/test/compile-fail/kindck-send-object1.rs b/src/test/compile-fail/kindck-send-object1.rs index 66865bbcc7e..673a6abc5f0 100644 --- a/src/test/compile-fail/kindck-send-object1.rs +++ b/src/test/compile-fail/kindck-send-object1.rs @@ -37,7 +37,7 @@ fn test61() { // them not ok fn test_71<'a>() { assert_send::>(); - //~^ ERROR : std::marker::Send` is not satisfied + //~^ ERROR `Dummy + 'a` cannot be sent between threads safely } fn main() { } diff --git a/src/test/compile-fail/kindck-send-object2.rs b/src/test/compile-fail/kindck-send-object2.rs index 51bc587d74f..3a935af2000 100644 --- a/src/test/compile-fail/kindck-send-object2.rs +++ b/src/test/compile-fail/kindck-send-object2.rs @@ -19,7 +19,8 @@ fn test50() { } fn test53() { - assert_send::>(); //~ ERROR : std::marker::Send` is not satisfied + assert_send::>(); + //~^ ERROR `Dummy` cannot be sent between threads safely } // ...unless they are properly bounded diff --git a/src/test/compile-fail/kindck-send-owned.rs b/src/test/compile-fail/kindck-send-owned.rs index 583381a1c28..e48460a8753 100644 --- a/src/test/compile-fail/kindck-send-owned.rs +++ b/src/test/compile-fail/kindck-send-owned.rs @@ -19,7 +19,8 @@ fn test32() { assert_send:: >(); } // but not if they own a bad thing fn test40() { - assert_send::>(); //~ ERROR : std::marker::Send` is not satisfied + assert_send::>(); + //~^ ERROR `*mut u8` cannot be sent between threads safely } fn main() { } diff --git a/src/test/compile-fail/kindck-send-unsafe.rs b/src/test/compile-fail/kindck-send-unsafe.rs index c717d1a72e0..99b995b0906 100644 --- a/src/test/compile-fail/kindck-send-unsafe.rs +++ b/src/test/compile-fail/kindck-send-unsafe.rs @@ -14,7 +14,7 @@ fn assert_send() { } fn test71<'a>() { assert_send::<*mut &'a isize>(); - //~^ ERROR `*mut &'a isize: std::marker::Send` is not satisfied + //~^ ERROR `*mut &'a isize` cannot be sent between threads safely } fn main() { diff --git a/src/test/compile-fail/no-send-res-ports.rs b/src/test/compile-fail/no-send-res-ports.rs index 334952cefa6..6825963c486 100644 --- a/src/test/compile-fail/no-send-res-ports.rs +++ b/src/test/compile-fail/no-send-res-ports.rs @@ -33,7 +33,7 @@ fn main() { let x = foo(Port(Rc::new(()))); thread::spawn(move|| { - //~^ ERROR `std::rc::Rc<()>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc<()>` cannot be sent between threads safely let y = x; println!("{:?}", y); }); diff --git a/src/test/compile-fail/no_send-enum.rs b/src/test/compile-fail/no_send-enum.rs index 902710e96e2..83f19ed19ef 100644 --- a/src/test/compile-fail/no_send-enum.rs +++ b/src/test/compile-fail/no_send-enum.rs @@ -24,5 +24,5 @@ fn bar(_: T) {} fn main() { let x = Foo::A(NoSend); bar(x); - //~^ ERROR `NoSend: std::marker::Send` is not satisfied + //~^ ERROR `NoSend` cannot be sent between threads safely } diff --git a/src/test/compile-fail/no_send-rc.rs b/src/test/compile-fail/no_send-rc.rs index f31d3787334..d3616d14422 100644 --- a/src/test/compile-fail/no_send-rc.rs +++ b/src/test/compile-fail/no_send-rc.rs @@ -15,5 +15,5 @@ fn bar(_: T) {} fn main() { let x = Rc::new(5); bar(x); - //~^ ERROR `std::rc::Rc<{integer}>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc<{integer}>` cannot be sent between threads safely } diff --git a/src/test/compile-fail/no_send-struct.rs b/src/test/compile-fail/no_send-struct.rs index b2ca4f9f5db..d38d993e7e8 100644 --- a/src/test/compile-fail/no_send-struct.rs +++ b/src/test/compile-fail/no_send-struct.rs @@ -23,5 +23,5 @@ fn bar(_: T) {} fn main() { let x = Foo { a: 5 }; bar(x); - //~^ ERROR `Foo: std::marker::Send` is not satisfied + //~^ ERROR `Foo` cannot be sent between threads safely } diff --git a/src/test/compile-fail/not-panic-safe-2.rs b/src/test/compile-fail/not-panic-safe-2.rs index 7107211fc91..d750851b719 100644 --- a/src/test/compile-fail/not-panic-safe-2.rs +++ b/src/test/compile-fail/not-panic-safe-2.rs @@ -18,6 +18,6 @@ fn assert() {} fn main() { assert::>>(); - //~^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied - //~^^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied + //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a } diff --git a/src/test/compile-fail/not-panic-safe-3.rs b/src/test/compile-fail/not-panic-safe-3.rs index 76c34e4dc0b..cd27b274258 100644 --- a/src/test/compile-fail/not-panic-safe-3.rs +++ b/src/test/compile-fail/not-panic-safe-3.rs @@ -18,6 +18,6 @@ fn assert() {} fn main() { assert::>>(); - //~^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied - //~^^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied + //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a } diff --git a/src/test/compile-fail/not-panic-safe-4.rs b/src/test/compile-fail/not-panic-safe-4.rs index 177a43e2a7f..956eca432c5 100644 --- a/src/test/compile-fail/not-panic-safe-4.rs +++ b/src/test/compile-fail/not-panic-safe-4.rs @@ -17,6 +17,6 @@ fn assert() {} fn main() { assert::<&RefCell>(); - //~^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied - //~^^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied + //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a } diff --git a/src/test/compile-fail/not-panic-safe-6.rs b/src/test/compile-fail/not-panic-safe-6.rs index f03e1d545a8..d0ca1db5212 100644 --- a/src/test/compile-fail/not-panic-safe-6.rs +++ b/src/test/compile-fail/not-panic-safe-6.rs @@ -17,6 +17,6 @@ fn assert() {} fn main() { assert::<*mut RefCell>(); - //~^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied - //~^^ ERROR `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied + //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a + //~| ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a } diff --git a/src/test/compile-fail/not-panic-safe.rs b/src/test/compile-fail/not-panic-safe.rs index ece8fa7dc47..0ebf3d3fed7 100644 --- a/src/test/compile-fail/not-panic-safe.rs +++ b/src/test/compile-fail/not-panic-safe.rs @@ -16,5 +16,6 @@ use std::panic::UnwindSafe; fn assert() {} fn main() { - assert::<&mut i32>(); //~ ERROR: UnwindSafe` is not satisfied + assert::<&mut i32>(); + //~^ ERROR the type `&mut i32` may not be safely transferred across an unwind boundary } diff --git a/src/test/compile-fail/range-1.rs b/src/test/compile-fail/range-1.rs index 58794e3b35d..3fb62b8d869 100644 --- a/src/test/compile-fail/range-1.rs +++ b/src/test/compile-fail/range-1.rs @@ -22,5 +22,5 @@ pub fn main() { // Unsized type. let arr: &[_] = &[1, 2, 3]; let range = *arr..; - //~^ ERROR `[{integer}]: std::marker::Sized` is not satisfied + //~^ ERROR `[{integer}]` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/range_traits-1.rs b/src/test/compile-fail/range_traits-1.rs index 32f9b83b6e2..78d3702b449 100644 --- a/src/test/compile-fail/range_traits-1.rs +++ b/src/test/compile-fail/range_traits-1.rs @@ -13,23 +13,23 @@ use std::ops::*; #[derive(Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)] struct AllTheRanges { a: Range, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord b: RangeTo, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord c: RangeFrom, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord d: RangeFull, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord e: RangeInclusive, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord f: RangeToInclusive, - //~^ ERROR PartialOrd - //~^^ ERROR Ord + //~^ ERROR can't compare + //~| ERROR Ord } fn main() {} diff --git a/src/test/compile-fail/str-idx.rs b/src/test/compile-fail/str-idx.rs index 2b2c23a3ce4..b5f1ffb977e 100644 --- a/src/test/compile-fail/str-idx.rs +++ b/src/test/compile-fail/str-idx.rs @@ -10,5 +10,5 @@ pub fn main() { let s: &str = "hello"; - let c: u8 = s[4]; //~ ERROR `str: std::ops::Index<{integer}>` is not satisfied + let c: u8 = s[4]; //~ ERROR the type `str` cannot be indexed by `{integer}` } diff --git a/src/test/compile-fail/str-mut-idx.rs b/src/test/compile-fail/str-mut-idx.rs index 219fcdfd702..c25d257d5f8 100644 --- a/src/test/compile-fail/str-mut-idx.rs +++ b/src/test/compile-fail/str-mut-idx.rs @@ -12,10 +12,10 @@ fn bot() -> T { loop {} } fn mutate(s: &mut str) { s[1..2] = bot(); - //~^ ERROR `str: std::marker::Sized` is not satisfied - //~| ERROR `str: std::marker::Sized` is not satisfied + //~^ ERROR `str` does not have a constant size known at compile-time + //~| ERROR `str` does not have a constant size known at compile-time s[1usize] = bot(); - //~^ ERROR `str: std::ops::IndexMut` is not satisfied + //~^ ERROR the type `str` cannot be mutably indexed by `usize` } pub fn main() {} diff --git a/src/test/compile-fail/substs-ppaux.rs b/src/test/compile-fail/substs-ppaux.rs index c857790e342..94d2a549a86 100644 --- a/src/test/compile-fail/substs-ppaux.rs +++ b/src/test/compile-fail/substs-ppaux.rs @@ -56,6 +56,6 @@ fn foo<'z>() where &'z (): Sized { //[normal]~| found type `fn() {foo::<'static>}` >::bar; - //[verbose]~^ ERROR `str: std::marker::Sized` is not satisfied - //[normal]~^^ ERROR `str: std::marker::Sized` is not satisfied + //[verbose]~^ ERROR `str` does not have a constant size known at compile-time + //[normal]~^^ ERROR `str` does not have a constant size known at compile-time } diff --git a/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs b/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs index 983c66ec1c4..effee4a70f3 100644 --- a/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs +++ b/src/test/compile-fail/trait-bounds-not-on-bare-trait.rs @@ -15,7 +15,7 @@ trait Foo { // This should emit the less confusing error, not the more confusing one. fn foo(_x: Foo + Send) { - //~^ ERROR the trait bound `Foo + std::marker::Send + 'static: std::marker::Sized` is not + //~^ ERROR `Foo + std::marker::Send + 'static` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/compile-fail/traits-negative-impls.rs b/src/test/compile-fail/traits-negative-impls.rs index 8014f92e173..a272686c535 100644 --- a/src/test/compile-fail/traits-negative-impls.rs +++ b/src/test/compile-fail/traits-negative-impls.rs @@ -31,8 +31,8 @@ fn dummy() { impl !Send for TestType {} Outer(TestType); - //~^ ERROR `dummy::TestType: std::marker::Send` is not satisfied - //~| ERROR `dummy::TestType: std::marker::Send` is not satisfied + //~^ ERROR `dummy::TestType` cannot be sent between threads safely + //~| ERROR `dummy::TestType` cannot be sent between threads safely } fn dummy1b() { @@ -40,7 +40,7 @@ fn dummy1b() { impl !Send for TestType {} is_send(TestType); - //~^ ERROR `dummy1b::TestType: std::marker::Send` is not satisfied + //~^ ERROR `dummy1b::TestType` cannot be sent between threads safely } fn dummy1c() { @@ -48,7 +48,7 @@ fn dummy1c() { impl !Send for TestType {} is_send((8, TestType)); - //~^ ERROR `dummy1c::TestType: std::marker::Send` is not satisfied + //~^ ERROR `dummy1c::TestType` cannot be sent between threads safely } fn dummy2() { @@ -56,7 +56,7 @@ fn dummy2() { impl !Send for TestType {} is_send(Box::new(TestType)); - //~^ ERROR `dummy2::TestType: std::marker::Send` is not satisfied + //~^ ERROR `dummy2::TestType` cannot be sent between threads safely } fn dummy3() { @@ -64,7 +64,7 @@ fn dummy3() { impl !Send for TestType {} is_send(Box::new(Outer2(TestType))); - //~^ ERROR `dummy3::TestType: std::marker::Send` is not satisfied + //~^ ERROR `dummy3::TestType` cannot be sent between threads safely } fn main() { @@ -74,5 +74,5 @@ fn main() { // This will complain about a missing Send impl because `Sync` is implement *just* // for T that are `Send`. Look at #20366 and #19950 is_sync(Outer2(TestType)); - //~^ ERROR `main::TestType: std::marker::Send` is not satisfied + //~^ ERROR `main::TestType` cannot be sent between threads safely } diff --git a/src/test/compile-fail/typeck-default-trait-impl-negation-send.rs b/src/test/compile-fail/typeck-default-trait-impl-negation-send.rs index 853718f1e77..65438e5df8e 100644 --- a/src/test/compile-fail/typeck-default-trait-impl-negation-send.rs +++ b/src/test/compile-fail/typeck-default-trait-impl-negation-send.rs @@ -27,5 +27,5 @@ fn is_send() {} fn main() { is_send::(); is_send::(); - //~^ ERROR `MyNotSendable: std::marker::Send` is not satisfied + //~^ ERROR `MyNotSendable` cannot be sent between threads safely } diff --git a/src/test/compile-fail/union/union-unsized.rs b/src/test/compile-fail/union/union-unsized.rs index a238eaf0525..32f22f052c1 100644 --- a/src/test/compile-fail/union/union-unsized.rs +++ b/src/test/compile-fail/union/union-unsized.rs @@ -11,13 +11,15 @@ #![feature(untagged_unions)] union U { - a: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + a: str, + //~^ ERROR `str` does not have a constant size known at compile-time b: u8, } union W { a: u8, - b: str, //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + b: str, + //~^ ERROR `str` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/compile-fail/unsized-bare-typaram.rs b/src/test/compile-fail/unsized-bare-typaram.rs index 3dcc7d248d7..be1f1dea28c 100644 --- a/src/test/compile-fail/unsized-bare-typaram.rs +++ b/src/test/compile-fail/unsized-bare-typaram.rs @@ -9,5 +9,6 @@ // except according to those terms. fn bar() { } -fn foo() { bar::() } //~ ERROR `T: std::marker::Sized` is not satisfied +fn foo() { bar::() } +//~^ ERROR `T` does not have a constant size known at compile-time fn main() { } diff --git a/src/test/compile-fail/unsized-enum.rs b/src/test/compile-fail/unsized-enum.rs index 5d791215f36..2041c69da54 100644 --- a/src/test/compile-fail/unsized-enum.rs +++ b/src/test/compile-fail/unsized-enum.rs @@ -15,7 +15,7 @@ fn not_sized() { } enum Foo { FooSome(U), FooNone } fn foo1() { not_sized::>() } // Hunky dory. fn foo2() { not_sized::>() } -//~^ ERROR `T: std::marker::Sized` is not satisfied +//~^ ERROR `T` does not have a constant size known at compile-time // // Not OK: `T` is not sized. diff --git a/src/test/compile-fail/unsized-inherent-impl-self-type.rs b/src/test/compile-fail/unsized-inherent-impl-self-type.rs index 4d0774f2ce4..5e5280ff3ea 100644 --- a/src/test/compile-fail/unsized-inherent-impl-self-type.rs +++ b/src/test/compile-fail/unsized-inherent-impl-self-type.rs @@ -14,7 +14,8 @@ struct S5(Y); -impl S5 { //~ ERROR E0277 +impl S5 { + //~^ ERROR `X` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/compile-fail/unsized-struct.rs b/src/test/compile-fail/unsized-struct.rs index bbefb2fcecd..830ac5d6c20 100644 --- a/src/test/compile-fail/unsized-struct.rs +++ b/src/test/compile-fail/unsized-struct.rs @@ -15,14 +15,14 @@ fn not_sized() { } struct Foo { data: T } fn foo1() { not_sized::>() } // Hunky dory. fn foo2() { not_sized::>() } -//~^ ERROR `T: std::marker::Sized` is not satisfied +//~^ ERROR `T` does not have a constant size known at compile-time // // Not OK: `T` is not sized. struct Bar { data: T } fn bar1() { not_sized::>() } fn bar2() { is_sized::>() } -//~^ ERROR `T: std::marker::Sized` is not satisfied +//~^ ERROR `T` does not have a constant size known at compile-time // // Not OK: `Bar` is not sized, but it should be. diff --git a/src/test/compile-fail/unsized-trait-impl-self-type.rs b/src/test/compile-fail/unsized-trait-impl-self-type.rs index c919bdf924f..9bf4cf7a0bb 100644 --- a/src/test/compile-fail/unsized-trait-impl-self-type.rs +++ b/src/test/compile-fail/unsized-trait-impl-self-type.rs @@ -17,7 +17,8 @@ trait T3 { struct S5(Y); -impl T3 for S5 { //~ ERROR E0277 +impl T3 for S5 { + //~^ ERROR `X` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/compile-fail/unsized-trait-impl-trait-arg.rs b/src/test/compile-fail/unsized-trait-impl-trait-arg.rs index ad5e4c2daef..b3a848954d1 100644 --- a/src/test/compile-fail/unsized-trait-impl-trait-arg.rs +++ b/src/test/compile-fail/unsized-trait-impl-trait-arg.rs @@ -16,7 +16,7 @@ trait T2 { } struct S4(Box); impl T2 for S4 { - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/compile-fail/unsized3.rs b/src/test/compile-fail/unsized3.rs index e96e0ea3aec..e08cf8280fd 100644 --- a/src/test/compile-fail/unsized3.rs +++ b/src/test/compile-fail/unsized3.rs @@ -15,7 +15,7 @@ use std::marker; // Unbounded. fn f1(x: &X) { f2::(x); - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time } fn f2(x: &X) { } @@ -26,7 +26,7 @@ trait T { } fn f3(x: &X) { f4::(x); - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time } fn f4(x: &X) { } @@ -41,20 +41,20 @@ struct S { fn f8(x1: &S, x2: &S) { f5(x1); - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time f6(x2); // ok } // Test some tuples. fn f9(x1: Box>) { f5(&(*x1, 34)); - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time } fn f10(x1: Box>) { f5(&(32, *x1)); - //~^ ERROR `X: std::marker::Sized` is not satisfied - //~| ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time + //~| ERROR `X` does not have a constant size known at compile-time } pub fn main() { diff --git a/src/test/compile-fail/unsized5.rs b/src/test/compile-fail/unsized5.rs index 3e6c9cc4061..1fb32da5e31 100644 --- a/src/test/compile-fail/unsized5.rs +++ b/src/test/compile-fail/unsized5.rs @@ -11,27 +11,33 @@ // Test `?Sized` types not allowed in fields (except the last one). struct S1 { - f1: X, //~ ERROR `X: std::marker::Sized` is not satisfied + f1: X, + //~^ ERROR `X` does not have a constant size known at compile-time f2: isize, } struct S2 { f: isize, - g: X, //~ ERROR `X: std::marker::Sized` is not satisfied + g: X, + //~^ ERROR `X` does not have a constant size known at compile-time h: isize, } struct S3 { - f: str, //~ ERROR `str: std::marker::Sized` is not satisfied + f: str, + //~^ ERROR `str` does not have a constant size known at compile-time g: [usize] } struct S4 { - f: [u8], //~ ERROR `[u8]: std::marker::Sized` is not satisfied + f: [u8], + //~^ ERROR `[u8]` does not have a constant size known at compile-time g: usize } enum E { - V1(X, isize), //~ERROR `X: std::marker::Sized` is not satisfied + V1(X, isize), + //~^ ERROR `X` does not have a constant size known at compile-time } enum F { - V2{f1: X, f: isize}, //~ERROR `X: std::marker::Sized` is not satisfied + V2{f1: X, f: isize}, + //~^ ERROR `X` does not have a constant size known at compile-time } pub fn main() { diff --git a/src/test/compile-fail/unsized6.rs b/src/test/compile-fail/unsized6.rs index dec8699f46e..7ce0e1eb4d8 100644 --- a/src/test/compile-fail/unsized6.rs +++ b/src/test/compile-fail/unsized6.rs @@ -14,28 +14,41 @@ trait T {} fn f1(x: &X) { let _: W; // <-- this is OK, no bindings created, no initializer. - let _: (isize, (X, isize)); //~ERROR `X: std::marker::Sized` is not satisfie - let y: Y; //~ERROR `Y: std::marker::Sized` is not satisfied - let y: (isize, (Z, usize)); //~ERROR `Z: std::marker::Sized` is not satisfied + let _: (isize, (X, isize)); + //~^ ERROR `X` does not have a constant size known at compile-time + let y: Y; + //~^ ERROR `Y` does not have a constant size known at compile-time + let y: (isize, (Z, usize)); + //~^ ERROR `Z` does not have a constant size known at compile-time } fn f2(x: &X) { - let y: X; //~ERROR `X: std::marker::Sized` is not satisfied - let y: (isize, (Y, isize)); //~ERROR `Y: std::marker::Sized` is not satisfied + let y: X; + //~^ ERROR `X` does not have a constant size known at compile-time + let y: (isize, (Y, isize)); + //~^ ERROR `Y` does not have a constant size known at compile-time } fn f3(x1: Box, x2: Box, x3: Box) { - let y: X = *x1; //~ERROR `X: std::marker::Sized` is not satisfied - let y = *x2; //~ERROR `X: std::marker::Sized` is not satisfied - let (y, z) = (*x3, 4); //~ERROR `X: std::marker::Sized` is not satisfied + let y: X = *x1; + //~^ ERROR `X` does not have a constant size known at compile-time + let y = *x2; + //~^ ERROR `X` does not have a constant size known at compile-time + let (y, z) = (*x3, 4); + //~^ ERROR `X` does not have a constant size known at compile-time } fn f4(x1: Box, x2: Box, x3: Box) { - let y: X = *x1; //~ERROR `X: std::marker::Sized` is not satisfied - let y = *x2; //~ERROR `X: std::marker::Sized` is not satisfied - let (y, z) = (*x3, 4); //~ERROR `X: std::marker::Sized` is not satisfied + let y: X = *x1; + //~^ ERROR `X` does not have a constant size known at compile-time + let y = *x2; + //~^ ERROR `X` does not have a constant size known at compile-time + let (y, z) = (*x3, 4); + //~^ ERROR `X` does not have a constant size known at compile-time } -fn g1(x: X) {} //~ERROR `X: std::marker::Sized` is not satisfied -fn g2(x: X) {} //~ERROR `X: std::marker::Sized` is not satisfied +fn g1(x: X) {} +//~^ ERROR `X` does not have a constant size known at compile-time +fn g2(x: X) {} +//~^ ERROR `X` does not have a constant size known at compile-time pub fn main() { } diff --git a/src/test/compile-fail/unsized7.rs b/src/test/compile-fail/unsized7.rs index 25868c594fe..8a3d78f0827 100644 --- a/src/test/compile-fail/unsized7.rs +++ b/src/test/compile-fail/unsized7.rs @@ -20,7 +20,7 @@ trait T1 { struct S3(Box); impl T1 for S3 { - //~^ ERROR `X: std::marker::Sized` is not satisfied + //~^ ERROR `X` does not have a constant size known at compile-time } fn main() { } diff --git a/src/test/ui/const-unsized.rs b/src/test/ui/const-unsized.rs index c6ce34b60ca..61ee622e21b 100644 --- a/src/test/ui/const-unsized.rs +++ b/src/test/ui/const-unsized.rs @@ -11,16 +11,16 @@ use std::fmt::Debug; const CONST_0: Debug+Sync = *(&0 as &(Debug+Sync)); -//~^ ERROR `std::fmt::Debug + std::marker::Sync + 'static: std::marker::Sized` is not satisfied +//~^ ERROR `std::fmt::Debug + std::marker::Sync + 'static` does not have a constant size known at const CONST_FOO: str = *"foo"; -//~^ ERROR `str: std::marker::Sized` is not satisfied +//~^ ERROR `str` does not have a constant size known at compile-time static STATIC_1: Debug+Sync = *(&1 as &(Debug+Sync)); -//~^ ERROR `std::fmt::Debug + std::marker::Sync + 'static: std::marker::Sized` is not satisfied +//~^ ERROR `std::fmt::Debug + std::marker::Sync + 'static` does not have a constant size known at static STATIC_BAR: str = *"bar"; -//~^ ERROR `str: std::marker::Sized` is not satisfied +//~^ ERROR `str` does not have a constant size known at compile-time fn main() { println!("{:?} {:?} {:?} {:?}", &CONST_0, &CONST_FOO, &STATIC_1, &STATIC_BAR); diff --git a/src/test/ui/const-unsized.stderr b/src/test/ui/const-unsized.stderr index 0bbb5debbba..ca434541cc2 100644 --- a/src/test/ui/const-unsized.stderr +++ b/src/test/ui/const-unsized.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `std::fmt::Debug + std::marker::Sync + 'static: std::marker::Sized` is not satisfied +error[E0277]: `std::fmt::Debug + std::marker::Sync + 'static` does not have a constant size known at compile-time --> $DIR/const-unsized.rs:13:29 | LL | const CONST_0: Debug+Sync = *(&0 as &(Debug+Sync)); @@ -7,7 +7,7 @@ LL | const CONST_0: Debug+Sync = *(&0 as &(Debug+Sync)); = help: the trait `std::marker::Sized` is not implemented for `std::fmt::Debug + std::marker::Sync + 'static` = note: constant expressions must have a statically known size -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/const-unsized.rs:16:24 | LL | const CONST_FOO: str = *"foo"; @@ -16,7 +16,7 @@ LL | const CONST_FOO: str = *"foo"; = help: the trait `std::marker::Sized` is not implemented for `str` = note: constant expressions must have a statically known size -error[E0277]: the trait bound `std::fmt::Debug + std::marker::Sync + 'static: std::marker::Sized` is not satisfied +error[E0277]: `std::fmt::Debug + std::marker::Sync + 'static` does not have a constant size known at compile-time --> $DIR/const-unsized.rs:19:31 | LL | static STATIC_1: Debug+Sync = *(&1 as &(Debug+Sync)); @@ -25,7 +25,7 @@ LL | static STATIC_1: Debug+Sync = *(&1 as &(Debug+Sync)); = help: the trait `std::marker::Sized` is not implemented for `std::fmt::Debug + std::marker::Sync + 'static` = note: constant expressions must have a statically known size -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/const-unsized.rs:22:26 | LL | static STATIC_BAR: str = *"bar"; diff --git a/src/test/ui/error-codes/E0277-2.rs b/src/test/ui/error-codes/E0277-2.rs index 4d1c50002a3..313aa1f706e 100644 --- a/src/test/ui/error-codes/E0277-2.rs +++ b/src/test/ui/error-codes/E0277-2.rs @@ -24,5 +24,5 @@ fn is_send() { } fn main() { is_send::(); - //~^ ERROR the trait bound `*const u8: std::marker::Send` is not satisfied in `Foo` + //~^ ERROR `*const u8` cannot be sent between threads safely } diff --git a/src/test/ui/error-codes/E0277-2.stderr b/src/test/ui/error-codes/E0277-2.stderr index bbe04cfc6e1..32776f028b4 100644 --- a/src/test/ui/error-codes/E0277-2.stderr +++ b/src/test/ui/error-codes/E0277-2.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `*const u8: std::marker::Send` is not satisfied in `Foo` +error[E0277]: `*const u8` cannot be sent between threads safely --> $DIR/E0277-2.rs:26:5 | LL | is_send::(); diff --git a/src/test/ui/error-codes/E0277.rs b/src/test/ui/error-codes/E0277.rs index b29e4357015..9ff2ef4da90 100644 --- a/src/test/ui/error-codes/E0277.rs +++ b/src/test/ui/error-codes/E0277.rs @@ -21,7 +21,7 @@ fn some_func(foo: T) { } fn f(p: Path) { } -//~^ ERROR the trait bound `[u8]: std::marker::Sized` is not satisfied in `std::path::Path` +//~^ ERROR `[u8]` does not have a constant size known at compile-time fn main() { some_func(5i32); diff --git a/src/test/ui/error-codes/E0277.stderr b/src/test/ui/error-codes/E0277.stderr index 477128d7d9f..9cfd42b9c19 100644 --- a/src/test/ui/error-codes/E0277.stderr +++ b/src/test/ui/error-codes/E0277.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied in `std::path::Path` +error[E0277]: `[u8]` does not have a constant size known at compile-time --> $DIR/E0277.rs:23:6 | LL | fn f(p: Path) { } diff --git a/src/test/ui/feature-gate-trivial_bounds.stderr b/src/test/ui/feature-gate-trivial_bounds.stderr index 9c2c80600b8..3c6d87e059a 100644 --- a/src/test/ui/feature-gate-trivial_bounds.stderr +++ b/src/test/ui/feature-gate-trivial_bounds.stderr @@ -87,7 +87,7 @@ LL | | } = help: see issue #48214 = help: add #![feature(trivial_bounds)] to the crate attributes to enable -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/feature-gate-trivial_bounds.rs:62:1 | LL | struct TwoStrs(str, str) where str: Sized; //~ ERROR @@ -97,7 +97,7 @@ LL | struct TwoStrs(str, str) where str: Sized; //~ ERROR = help: see issue #48214 = help: add #![feature(trivial_bounds)] to the crate attributes to enable -error[E0277]: the trait bound `A + 'static: std::marker::Sized` is not satisfied in `Dst` +error[E0277]: `A + 'static` does not have a constant size known at compile-time --> $DIR/feature-gate-trivial_bounds.rs:65:1 | LL | / fn unsized_local() where Dst: Sized { //~ ERROR @@ -110,7 +110,7 @@ LL | | } = help: see issue #48214 = help: add #![feature(trivial_bounds)] to the crate attributes to enable -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/feature-gate-trivial_bounds.rs:69:1 | LL | / fn return_str() -> str where str: Sized { //~ ERROR diff --git a/src/test/ui/generator/sized-yield.rs b/src/test/ui/generator/sized-yield.rs index a1c8ca77e41..165e2702597 100644 --- a/src/test/ui/generator/sized-yield.rs +++ b/src/test/ui/generator/sized-yield.rs @@ -14,8 +14,10 @@ use std::ops::Generator; fn main() { let s = String::from("foo"); - let mut gen = move || { //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + let mut gen = move || { + //~^ ERROR `str` does not have a constant size known at compile-time yield s[..]; }; - unsafe { gen.resume(); } //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied + unsafe { gen.resume(); } + //~^ ERROR `str` does not have a constant size known at compile-time } diff --git a/src/test/ui/generator/sized-yield.stderr b/src/test/ui/generator/sized-yield.stderr index 957fac172c2..45f06659053 100644 --- a/src/test/ui/generator/sized-yield.stderr +++ b/src/test/ui/generator/sized-yield.stderr @@ -1,8 +1,9 @@ -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/sized-yield.rs:17:26 | -LL | let mut gen = move || { //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied +LL | let mut gen = move || { | __________________________^ +LL | | //~^ ERROR `str` does not have a constant size known at compile-time LL | | yield s[..]; LL | | }; | |____^ `str` does not have a constant size known at compile-time @@ -10,10 +11,10 @@ LL | | }; = help: the trait `std::marker::Sized` is not implemented for `str` = note: the yield type of a generator must have a statically known size -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied - --> $DIR/sized-yield.rs:20:17 +error[E0277]: `str` does not have a constant size known at compile-time + --> $DIR/sized-yield.rs:21:17 | -LL | unsafe { gen.resume(); } //~ ERROR the trait bound `str: std::marker::Sized` is not satisfied +LL | unsafe { gen.resume(); } | ^^^^^^ `str` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `str` diff --git a/src/test/ui/impl-trait/auto-trait-leak.rs b/src/test/ui/impl-trait/auto-trait-leak.rs index abb3682a498..f6b64b394fc 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.rs +++ b/src/test/ui/impl-trait/auto-trait-leak.rs @@ -25,7 +25,7 @@ fn cycle1() -> impl Clone { //~^ ERROR cycle detected //~| ERROR cycle detected send(cycle2().clone()); - //~^ ERROR Send` is not satisfied + //~^ ERROR `std::rc::Rc` cannot be sent between threads safely Rc::new(Cell::new(5)) } diff --git a/src/test/ui/impl-trait/auto-trait-leak.stderr b/src/test/ui/impl-trait/auto-trait-leak.stderr index 4537c96c4ab..b34facd2d39 100644 --- a/src/test/ui/impl-trait/auto-trait-leak.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak.stderr @@ -47,7 +47,7 @@ LL | fn cycle2() -> impl Clone { | ^^^^^^^^^^^^^^^^^^^^^^^^^ = note: ...which again requires processing `cycle1::{{exist-impl-Trait}}`, completing the cycle -error[E0277]: the trait bound `std::rc::Rc: std::marker::Send` is not satisfied in `impl std::clone::Clone` +error[E0277]: `std::rc::Rc` cannot be sent between threads safely --> $DIR/auto-trait-leak.rs:27:5 | LL | send(cycle2().clone()); diff --git a/src/test/ui/impl-trait/auto-trait-leak2.rs b/src/test/ui/impl-trait/auto-trait-leak2.rs index 16310e67f1b..3c61543a711 100644 --- a/src/test/ui/impl-trait/auto-trait-leak2.rs +++ b/src/test/ui/impl-trait/auto-trait-leak2.rs @@ -23,10 +23,10 @@ fn send(_: T) {} fn main() { send(before()); - //~^ ERROR the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely send(after()); - //~^ ERROR the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied + //~^ ERROR `std::rc::Rc>` cannot be sent between threads safely } // Deferred path, main has to wait until typeck finishes, diff --git a/src/test/ui/impl-trait/auto-trait-leak2.stderr b/src/test/ui/impl-trait/auto-trait-leak2.stderr index 59623aed3d2..fb00c41f79c 100644 --- a/src/test/ui/impl-trait/auto-trait-leak2.stderr +++ b/src/test/ui/impl-trait/auto-trait-leak2.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied in `impl std::ops::Fn<(i32,)>` +error[E0277]: `std::rc::Rc>` cannot be sent between threads safely --> $DIR/auto-trait-leak2.rs:25:5 | LL | send(before()); @@ -13,7 +13,7 @@ note: required by `send` LL | fn send(_: T) {} | ^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `std::rc::Rc>: std::marker::Send` is not satisfied in `impl std::ops::Fn<(i32,)>` +error[E0277]: `std::rc::Rc>` cannot be sent between threads safely --> $DIR/auto-trait-leak2.rs:28:5 | LL | send(after()); diff --git a/src/test/ui/interior-mutability/interior-mutability.rs b/src/test/ui/interior-mutability/interior-mutability.rs index a772d1f90cc..b0288463e91 100644 --- a/src/test/ui/interior-mutability/interior-mutability.rs +++ b/src/test/ui/interior-mutability/interior-mutability.rs @@ -12,5 +12,6 @@ use std::cell::Cell; use std::panic::catch_unwind; fn main() { let mut x = Cell::new(22); - catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound + catch_unwind(|| { x.set(23); }); + //~^ ERROR the type `std::cell::UnsafeCell` may contain interior mutability and a } diff --git a/src/test/ui/interior-mutability/interior-mutability.stderr b/src/test/ui/interior-mutability/interior-mutability.stderr index 4c489c5964b..f2aecc55ccb 100644 --- a/src/test/ui/interior-mutability/interior-mutability.stderr +++ b/src/test/ui/interior-mutability/interior-mutability.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `std::cell::UnsafeCell: std::panic::RefUnwindSafe` is not satisfied in `std::cell::Cell` +error[E0277]: the type `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary --> $DIR/interior-mutability.rs:15:5 | -LL | catch_unwind(|| { x.set(23); }); //~ ERROR the trait bound - | ^^^^^^^^^^^^ the type std::cell::UnsafeCell may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary +LL | catch_unwind(|| { x.set(23); }); + | ^^^^^^^^^^^^ `std::cell::UnsafeCell` may contain interior mutability and a reference may not be safely transferrable across a catch_unwind boundary | = help: within `std::cell::Cell`, the trait `std::panic::RefUnwindSafe` is not implemented for `std::cell::UnsafeCell` = note: required because it appears within the type `std::cell::Cell` diff --git a/src/test/ui/mismatched_types/binops.rs b/src/test/ui/mismatched_types/binops.rs index 3f2cb59b11d..86785f24f36 100644 --- a/src/test/ui/mismatched_types/binops.rs +++ b/src/test/ui/mismatched_types/binops.rs @@ -13,6 +13,6 @@ fn main() { 2 as usize - Some(1); //~ ERROR cannot subtract `std::option::Option<{integer}>` from `usize` 3 * (); //~ ERROR cannot multiply `()` to `{integer}` 4 / ""; //~ ERROR cannot divide `{integer}` by `&str` - 5 < String::new(); //~ ERROR is not satisfied - 6 == Ok(1); //~ ERROR is not satisfied + 5 < String::new(); //~ ERROR can't compare `{integer}` with `std::string::String` + 6 == Ok(1); //~ ERROR can't compare `{integer}` with `std::result::Result<{integer}, _>` } diff --git a/src/test/ui/mismatched_types/binops.stderr b/src/test/ui/mismatched_types/binops.stderr index 9d23b256fd3..4c6d95efadb 100644 --- a/src/test/ui/mismatched_types/binops.stderr +++ b/src/test/ui/mismatched_types/binops.stderr @@ -30,19 +30,19 @@ LL | 4 / ""; //~ ERROR cannot divide `{integer}` by `&str` | = help: the trait `std::ops::Div<&str>` is not implemented for `{integer}` -error[E0277]: the trait bound `{integer}: std::cmp::PartialOrd` is not satisfied +error[E0277]: can't compare `{integer}` with `std::string::String` --> $DIR/binops.rs:16:7 | -LL | 5 < String::new(); //~ ERROR is not satisfied - | ^ can't compare `{integer}` with `std::string::String` +LL | 5 < String::new(); //~ ERROR can't compare `{integer}` with `std::string::String` + | ^ no implementation for `{integer} < std::string::String` and `{integer} > std::string::String` | = help: the trait `std::cmp::PartialOrd` is not implemented for `{integer}` -error[E0277]: the trait bound `{integer}: std::cmp::PartialEq>` is not satisfied +error[E0277]: can't compare `{integer}` with `std::result::Result<{integer}, _>` --> $DIR/binops.rs:17:7 | -LL | 6 == Ok(1); //~ ERROR is not satisfied - | ^^ can't compare `{integer}` with `std::result::Result<{integer}, _>` +LL | 6 == Ok(1); //~ ERROR can't compare `{integer}` with `std::result::Result<{integer}, _>` + | ^^ no implementation for `{integer} == std::result::Result<{integer}, _>` | = help: the trait `std::cmp::PartialEq>` is not implemented for `{integer}` diff --git a/src/test/ui/mismatched_types/cast-rfc0401.rs b/src/test/ui/mismatched_types/cast-rfc0401.rs index 15388b3a764..7ec9593d2de 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.rs +++ b/src/test/ui/mismatched_types/cast-rfc0401.rs @@ -60,7 +60,7 @@ fn main() let _ = 42usize as *const [u8]; //~ ERROR is invalid let _ = v as *const [u8]; //~ ERROR cannot cast - let _ = fat_v as *const Foo; //~ ERROR is not satisfied + let _ = fat_v as *const Foo; //~ ERROR `[u8]` does not have a constant size known at compile-time let _ = foo as *const str; //~ ERROR is invalid let _ = foo as *mut str; //~ ERROR is invalid let _ = main as *mut str; //~ ERROR is invalid @@ -69,7 +69,7 @@ fn main() let _ = fat_sv as usize; //~ ERROR is invalid let a : *const str = "hello"; - let _ = a as *const Foo; //~ ERROR is not satisfied + let _ = a as *const Foo; //~ ERROR `str` does not have a constant size known at compile-time // check no error cascade let _ = main.f as *const u32; //~ ERROR no field diff --git a/src/test/ui/mismatched_types/cast-rfc0401.stderr b/src/test/ui/mismatched_types/cast-rfc0401.stderr index 7931e7ff07f..2b00c20e201 100644 --- a/src/test/ui/mismatched_types/cast-rfc0401.stderr +++ b/src/test/ui/mismatched_types/cast-rfc0401.stderr @@ -216,19 +216,19 @@ LL | let _ = cf as *const Bar; //~ ERROR is invalid | = note: vtable kinds may not match -error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied +error[E0277]: `[u8]` does not have a constant size known at compile-time --> $DIR/cast-rfc0401.rs:63:13 | -LL | let _ = fat_v as *const Foo; //~ ERROR is not satisfied +LL | let _ = fat_v as *const Foo; //~ ERROR `[u8]` does not have a constant size known at compile-time | ^^^^^ `[u8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: required for the cast to the object type `Foo` -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/cast-rfc0401.rs:72:13 | -LL | let _ = a as *const Foo; //~ ERROR is not satisfied +LL | let _ = a as *const Foo; //~ ERROR `str` does not have a constant size known at compile-time | ^ `str` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `str` diff --git a/src/test/ui/partialeq_help.stderr b/src/test/ui/partialeq_help.stderr index c813b64d40b..43f13d45684 100644 --- a/src/test/ui/partialeq_help.stderr +++ b/src/test/ui/partialeq_help.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `&T: std::cmp::PartialEq` is not satisfied +error[E0277]: can't compare `&T` with `T` --> $DIR/partialeq_help.rs:12:7 | LL | a == b; //~ ERROR E0277 - | ^^ can't compare `&T` with `T` + | ^^ no implementation for `&T == T` | = help: the trait `std::cmp::PartialEq` is not implemented for `&T` = help: consider adding a `where &T: std::cmp::PartialEq` bound diff --git a/src/test/ui/resolve/issue-5035-2.rs b/src/test/ui/resolve/issue-5035-2.rs index 83ff95cc2ea..f6cdb05394a 100644 --- a/src/test/ui/resolve/issue-5035-2.rs +++ b/src/test/ui/resolve/issue-5035-2.rs @@ -11,6 +11,7 @@ trait I {} type K = I+'static; -fn foo(_x: K) {} //~ ERROR: `I + 'static: std::marker::Sized` is not satisfied +fn foo(_x: K) {} +//~^ ERROR `I + 'static` does not have a constant size known at compile-time fn main() {} diff --git a/src/test/ui/resolve/issue-5035-2.stderr b/src/test/ui/resolve/issue-5035-2.stderr index 92309274e84..554e97a1281 100644 --- a/src/test/ui/resolve/issue-5035-2.stderr +++ b/src/test/ui/resolve/issue-5035-2.stderr @@ -1,7 +1,7 @@ -error[E0277]: the trait bound `I + 'static: std::marker::Sized` is not satisfied +error[E0277]: `I + 'static` does not have a constant size known at compile-time --> $DIR/issue-5035-2.rs:14:8 | -LL | fn foo(_x: K) {} //~ ERROR: `I + 'static: std::marker::Sized` is not satisfied +LL | fn foo(_x: K) {} | ^^ `I + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `I + 'static` diff --git a/src/test/ui/suggestions/str-array-assignment.rs b/src/test/ui/suggestions/str-array-assignment.rs index b70028bd926..f6b75981a66 100644 --- a/src/test/ui/suggestions/str-array-assignment.rs +++ b/src/test/ui/suggestions/str-array-assignment.rs @@ -15,7 +15,7 @@ fn main() { let u: &str = if true { s[..2] } else { s }; //~^ ERROR mismatched types let v = s[..2]; - //~^ ERROR the trait bound `str: std::marker::Sized` is not satisfied + //~^ ERROR `str` does not have a constant size known at compile-time let w: &str = s[..2]; //~^ ERROR mismatched types } diff --git a/src/test/ui/suggestions/str-array-assignment.stderr b/src/test/ui/suggestions/str-array-assignment.stderr index 76db882742a..91e86e344b4 100644 --- a/src/test/ui/suggestions/str-array-assignment.stderr +++ b/src/test/ui/suggestions/str-array-assignment.stderr @@ -19,7 +19,7 @@ LL | let u: &str = if true { s[..2] } else { s }; = note: expected type `&str` found type `str` -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/str-array-assignment.rs:17:7 | LL | let v = s[..2]; diff --git a/src/test/ui/trait-suggest-where-clause.rs b/src/test/ui/trait-suggest-where-clause.rs index 5dcb4c8a220..7962dbea371 100644 --- a/src/test/ui/trait-suggest-where-clause.rs +++ b/src/test/ui/trait-suggest-where-clause.rs @@ -15,10 +15,10 @@ struct Misc(T); fn check() { // suggest a where-clause, if needed mem::size_of::(); - //~^ ERROR `U: std::marker::Sized` is not satisfied + //~^ ERROR `U` does not have a constant size known at compile-time mem::size_of::>(); - //~^ ERROR `U: std::marker::Sized` is not satisfied + //~^ ERROR `U` does not have a constant size known at compile-time // ... even if T occurs as a type parameter @@ -36,10 +36,10 @@ fn check() { // ... and also not if the error is not related to the type mem::size_of::<[T]>(); - //~^ ERROR `[T]: std::marker::Sized` is not satisfied + //~^ ERROR `[T]` does not have a constant size known at compile-time mem::size_of::<[&U]>(); - //~^ ERROR `[&U]: std::marker::Sized` is not satisfied + //~^ ERROR `[&U]` does not have a constant size known at compile-time } fn main() { diff --git a/src/test/ui/trait-suggest-where-clause.stderr b/src/test/ui/trait-suggest-where-clause.stderr index abd9f5a8b73..d31e9288037 100644 --- a/src/test/ui/trait-suggest-where-clause.stderr +++ b/src/test/ui/trait-suggest-where-clause.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `U: std::marker::Sized` is not satisfied +error[E0277]: `U` does not have a constant size known at compile-time --> $DIR/trait-suggest-where-clause.rs:17:5 | LL | mem::size_of::(); @@ -8,7 +8,7 @@ LL | mem::size_of::(); = help: consider adding a `where U: std::marker::Sized` bound = note: required by `std::mem::size_of` -error[E0277]: the trait bound `U: std::marker::Sized` is not satisfied in `Misc` +error[E0277]: `U` does not have a constant size known at compile-time --> $DIR/trait-suggest-where-clause.rs:20:5 | LL | mem::size_of::>(); @@ -45,7 +45,7 @@ LL | as From>::from; | = note: required by `std::convert::From::from` -error[E0277]: the trait bound `[T]: std::marker::Sized` is not satisfied +error[E0277]: `[T]` does not have a constant size known at compile-time --> $DIR/trait-suggest-where-clause.rs:38:5 | LL | mem::size_of::<[T]>(); @@ -54,7 +54,7 @@ LL | mem::size_of::<[T]>(); = help: the trait `std::marker::Sized` is not implemented for `[T]` = note: required by `std::mem::size_of` -error[E0277]: the trait bound `[&U]: std::marker::Sized` is not satisfied +error[E0277]: `[&U]` does not have a constant size known at compile-time --> $DIR/trait-suggest-where-clause.rs:41:5 | LL | mem::size_of::<[&U]>(); diff --git a/src/test/ui/trivial-bounds-leak.stderr b/src/test/ui/trivial-bounds-leak.stderr index df91ba0dd2a..d54414110b1 100644 --- a/src/test/ui/trivial-bounds-leak.stderr +++ b/src/test/ui/trivial-bounds-leak.stderr @@ -1,4 +1,4 @@ -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied +error[E0277]: `str` does not have a constant size known at compile-time --> $DIR/trivial-bounds-leak.rs:22:25 | LL | fn cant_return_str() -> str { //~ ERROR diff --git a/src/test/ui/type-check-defaults.rs b/src/test/ui/type-check-defaults.rs index f916df5d32d..92a45db0689 100644 --- a/src/test/ui/type-check-defaults.rs +++ b/src/test/ui/type-check-defaults.rs @@ -14,24 +14,24 @@ use std::ops::Add; struct Foo>(T, U); struct WellFormed>(Z); -//~^ error: the trait bound `i32: std::iter::FromIterator` is not satisfied [E0277] +//~^ ERROR a collection of type `i32` cannot be built from an iterator over elements of type `i32` struct WellFormedNoBounds>(Z); -//~^ error: the trait bound `i32: std::iter::FromIterator` is not satisfied [E0277] +//~^ ERROR a collection of type `i32` cannot be built from an iterator over elements of type `i32` struct Bounds(T); -//~^ error: the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] struct WhereClause(T) where T: Copy; -//~^ error: the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] trait TraitBound {} -//~^ error: the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `std::string::String: std::marker::Copy` is not satisfied [E0277] trait Super { } trait Base: Super { } -//~^ error: the trait bound `T: std::marker::Copy` is not satisfied [E0277] +//~^ ERROR the trait bound `T: std::marker::Copy` is not satisfied [E0277] trait ProjectionPred> where T::Item : Add {} -//~^ error: cannot add `u8` to `i32` [E0277] +//~^ ERROR cannot add `u8` to `i32` [E0277] fn main() { } diff --git a/src/test/ui/type-check-defaults.stderr b/src/test/ui/type-check-defaults.stderr index a2d6e53df05..aa124110243 100644 --- a/src/test/ui/type-check-defaults.stderr +++ b/src/test/ui/type-check-defaults.stderr @@ -1,8 +1,8 @@ -error[E0277]: the trait bound `i32: std::iter::FromIterator` is not satisfied +error[E0277]: a collection of type `i32` cannot be built from an iterator over elements of type `i32` --> $DIR/type-check-defaults.rs:16:19 | LL | struct WellFormed>(Z); - | ^ a collection of type `i32` cannot be built from an iterator over elements of type `i32` + | ^ a collection of type `i32` cannot be built from `std::iter::Iterator` | = help: the trait `std::iter::FromIterator` is not implemented for `i32` note: required by `Foo` @@ -11,11 +11,11 @@ note: required by `Foo` LL | struct Foo>(T, U); | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ -error[E0277]: the trait bound `i32: std::iter::FromIterator` is not satisfied +error[E0277]: a collection of type `i32` cannot be built from an iterator over elements of type `i32` --> $DIR/type-check-defaults.rs:18:27 | LL | struct WellFormedNoBounds>(Z); - | ^ a collection of type `i32` cannot be built from an iterator over elements of type `i32` + | ^ a collection of type `i32` cannot be built from `std::iter::Iterator` | = help: the trait `std::iter::FromIterator` is not implemented for `i32` note: required by `Foo` diff --git a/src/test/ui/union/union-sized-field.rs b/src/test/ui/union/union-sized-field.rs index 8999f1e0930..e40c6d11cb3 100644 --- a/src/test/ui/union/union-sized-field.rs +++ b/src/test/ui/union/union-sized-field.rs @@ -11,16 +11,19 @@ #![feature(untagged_unions)] union Foo { - value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied + value: T, + //~^ ERROR `T` does not have a constant size known at compile-time } struct Foo2 { - value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied + value: T, + //~^ ERROR `T` does not have a constant size known at compile-time t: u32, } enum Foo3 { - Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied + Value(T), + //~^ ERROR `T` does not have a constant size known at compile-time } fn main() {} diff --git a/src/test/ui/union/union-sized-field.stderr b/src/test/ui/union/union-sized-field.stderr index ba80af6c7e0..ce6de86cff9 100644 --- a/src/test/ui/union/union-sized-field.stderr +++ b/src/test/ui/union/union-sized-field.stderr @@ -1,27 +1,27 @@ -error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied +error[E0277]: `T` does not have a constant size known at compile-time --> $DIR/union-sized-field.rs:14:5 | -LL | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied +LL | value: T, | ^^^^^^^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` = help: consider adding a `where T: std::marker::Sized` bound = note: no field of a union may have a dynamically sized type -error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied - --> $DIR/union-sized-field.rs:18:5 +error[E0277]: `T` does not have a constant size known at compile-time + --> $DIR/union-sized-field.rs:19:5 | -LL | value: T, //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied +LL | value: T, | ^^^^^^^^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` = help: consider adding a `where T: std::marker::Sized` bound = note: only the last field of a struct may have a dynamically sized type -error[E0277]: the trait bound `T: std::marker::Sized` is not satisfied - --> $DIR/union-sized-field.rs:23:11 +error[E0277]: `T` does not have a constant size known at compile-time + --> $DIR/union-sized-field.rs:25:11 | -LL | Value(T), //~ ERROR the trait bound `T: std::marker::Sized` is not satisfied +LL | Value(T), | ^ `T` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `T` diff --git a/src/test/ui/unsized-enum2.rs b/src/test/ui/unsized-enum2.rs index 95fc3243fbe..4e42b92289b 100644 --- a/src/test/ui/unsized-enum2.rs +++ b/src/test/ui/unsized-enum2.rs @@ -30,37 +30,54 @@ struct Path4(PathHelper4); enum E { // parameter - VA(W), //~ ERROR `W: std::marker::Sized` is not satisfied - VB{x: X}, //~ ERROR `X: std::marker::Sized` is not satisfied - VC(isize, Y), //~ ERROR `Y: std::marker::Sized` is not satisfied - VD{u: isize, x: Z}, //~ ERROR `Z: std::marker::Sized` is not satisfied + VA(W), + //~^ ERROR `W` does not have a constant size known at compile-time + VB{x: X}, + //~^ ERROR `X` does not have a constant size known at compile-time + VC(isize, Y), + //~^ ERROR `Y` does not have a constant size known at compile-time + VD{u: isize, x: Z}, + //~^ ERROR `Z` does not have a constant size known at compile-time // slice / str - VE([u8]), //~ ERROR `[u8]: std::marker::Sized` is not satisfied - VF{x: str}, //~ ERROR `str: std::marker::Sized` is not satisfied - VG(isize, [f32]), //~ ERROR `[f32]: std::marker::Sized` is not satisfied - VH{u: isize, x: [u32]}, //~ ERROR `[u32]: std::marker::Sized` is not satisfied + VE([u8]), + //~^ ERROR `[u8]` does not have a constant size known at compile-time + VF{x: str}, + //~^ ERROR `str` does not have a constant size known at compile-time + VG(isize, [f32]), + //~^ ERROR `[f32]` does not have a constant size known at compile-time + VH{u: isize, x: [u32]}, + //~^ ERROR `[u32]` does not have a constant size known at compile-time // unsized struct - VI(Path1), //~ ERROR `PathHelper1 + 'static: std::marker::Sized` is not satisfied - VJ{x: Path2}, //~ ERROR `PathHelper2 + 'static: std::marker::Sized` is not satisfied - VK(isize, Path3), //~ ERROR `PathHelper3 + 'static: std::marker::Sized` is not satisfied - VL{u: isize, x: Path4}, //~ ERROR `PathHelper4 + 'static: std::marker::Sized` is not satisfied + VI(Path1), + //~^ ERROR `PathHelper1 + 'static` does not have a constant size known at compile-time + VJ{x: Path2}, + //~^ ERROR `PathHelper2 + 'static` does not have a constant size known at compile-time + VK(isize, Path3), + //~^ ERROR `PathHelper3 + 'static` does not have a constant size known at compile-time + VL{u: isize, x: Path4}, + //~^ ERROR `PathHelper4 + 'static` does not have a constant size known at compile-time // plain trait - VM(Foo), //~ ERROR `Foo + 'static: std::marker::Sized` is not satisfied - VN{x: Bar}, //~ ERROR `Bar + 'static: std::marker::Sized` is not satisfied - VO(isize, FooBar), //~ ERROR `FooBar + 'static: std::marker::Sized` is not satisfied - VP{u: isize, x: BarFoo}, //~ ERROR `BarFoo + 'static: std::marker::Sized` is not satisfied + VM(Foo), + //~^ ERROR `Foo + 'static` does not have a constant size known at compile-time + VN{x: Bar}, + //~^ ERROR `Bar + 'static` does not have a constant size known at compile-time + VO(isize, FooBar), + //~^ ERROR `FooBar + 'static` does not have a constant size known at compile-time + VP{u: isize, x: BarFoo}, + //~^ ERROR `BarFoo + 'static` does not have a constant size known at compile-time // projected - VQ(<&'static [i8] as Deref>::Target), //~ ERROR `[i8]: std::marker::Sized` is not satisfied + VQ(<&'static [i8] as Deref>::Target), + //~^ ERROR `[i8]` does not have a constant size known at compile-time VR{x: <&'static [char] as Deref>::Target}, - //~^ ERROR `[char]: std::marker::Sized` is not satisfied + //~^ ERROR `[char]` does not have a constant size known at compile-time VS(isize, <&'static [f64] as Deref>::Target), - //~^ ERROR `[f64]: std::marker::Sized` is not satisfied + //~^ ERROR `[f64]` does not have a constant size known at compile-time VT{u: isize, x: <&'static [i32] as Deref>::Target}, - //~^ ERROR `[i32]: std::marker::Sized` is not satisfied + //~^ ERROR `[i32]` does not have a constant size known at compile-time } diff --git a/src/test/ui/unsized-enum2.stderr b/src/test/ui/unsized-enum2.stderr index 0e18efbf9da..2784bf5af1b 100644 --- a/src/test/ui/unsized-enum2.stderr +++ b/src/test/ui/unsized-enum2.stderr @@ -1,126 +1,126 @@ -error[E0277]: the trait bound `W: std::marker::Sized` is not satisfied +error[E0277]: `W` does not have a constant size known at compile-time --> $DIR/unsized-enum2.rs:33:8 | -LL | VA(W), //~ ERROR `W: std::marker::Sized` is not satisfied +LL | VA(W), | ^ `W` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `W` = help: consider adding a `where W: std::marker::Sized` bound = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `X: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:34:8 +error[E0277]: `X` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:35:8 | -LL | VB{x: X}, //~ ERROR `X: std::marker::Sized` is not satisfied +LL | VB{x: X}, | ^^^^ `X` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `X` = help: consider adding a `where X: std::marker::Sized` bound = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `Y: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:35:15 +error[E0277]: `Y` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:37:15 | -LL | VC(isize, Y), //~ ERROR `Y: std::marker::Sized` is not satisfied +LL | VC(isize, Y), | ^ `Y` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Y` = help: consider adding a `where Y: std::marker::Sized` bound = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `Z: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:36:18 +error[E0277]: `Z` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:39:18 | -LL | VD{u: isize, x: Z}, //~ ERROR `Z: std::marker::Sized` is not satisfied +LL | VD{u: isize, x: Z}, | ^^^^ `Z` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Z` = help: consider adding a `where Z: std::marker::Sized` bound = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[u8]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:39:8 +error[E0277]: `[u8]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:43:8 | -LL | VE([u8]), //~ ERROR `[u8]: std::marker::Sized` is not satisfied +LL | VE([u8]), | ^^^^ `[u8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u8]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `str: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:40:8 +error[E0277]: `str` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:45:8 | -LL | VF{x: str}, //~ ERROR `str: std::marker::Sized` is not satisfied +LL | VF{x: str}, | ^^^^^^ `str` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `str` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[f32]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:41:15 +error[E0277]: `[f32]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:47:15 | -LL | VG(isize, [f32]), //~ ERROR `[f32]: std::marker::Sized` is not satisfied +LL | VG(isize, [f32]), | ^^^^^ `[f32]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[f32]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[u32]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:42:18 +error[E0277]: `[u32]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:49:18 | -LL | VH{u: isize, x: [u32]}, //~ ERROR `[u32]: std::marker::Sized` is not satisfied +LL | VH{u: isize, x: [u32]}, | ^^^^^^^^ `[u32]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[u32]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `Foo + 'static: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:51:8 +error[E0277]: `Foo + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:63:8 | -LL | VM(Foo), //~ ERROR `Foo + 'static: std::marker::Sized` is not satisfied +LL | VM(Foo), | ^^^ `Foo + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Foo + 'static` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `Bar + 'static: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:52:8 +error[E0277]: `Bar + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:65:8 | -LL | VN{x: Bar}, //~ ERROR `Bar + 'static: std::marker::Sized` is not satisfied +LL | VN{x: Bar}, | ^^^^^^ `Bar + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `Bar + 'static` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `FooBar + 'static: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:53:15 +error[E0277]: `FooBar + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:67:15 | -LL | VO(isize, FooBar), //~ ERROR `FooBar + 'static: std::marker::Sized` is not satisfied +LL | VO(isize, FooBar), | ^^^^^^ `FooBar + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `FooBar + 'static` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `BarFoo + 'static: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:54:18 +error[E0277]: `BarFoo + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:69:18 | -LL | VP{u: isize, x: BarFoo}, //~ ERROR `BarFoo + 'static: std::marker::Sized` is not satisfied +LL | VP{u: isize, x: BarFoo}, | ^^^^^^^^^ `BarFoo + 'static` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `BarFoo + 'static` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[i8]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:57:8 +error[E0277]: `[i8]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:73:8 | -LL | VQ(<&'static [i8] as Deref>::Target), //~ ERROR `[i8]: std::marker::Sized` is not satisfied +LL | VQ(<&'static [i8] as Deref>::Target), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[i8]` does not have a constant size known at compile-time | = help: the trait `std::marker::Sized` is not implemented for `[i8]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[char]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:58:8 +error[E0277]: `[char]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:75:8 | LL | VR{x: <&'static [char] as Deref>::Target}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[char]` does not have a constant size known at compile-time @@ -128,8 +128,8 @@ LL | VR{x: <&'static [char] as Deref>::Target}, = help: the trait `std::marker::Sized` is not implemented for `[char]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[f64]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:60:15 +error[E0277]: `[f64]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:77:15 | LL | VS(isize, <&'static [f64] as Deref>::Target), | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[f64]` does not have a constant size known at compile-time @@ -137,8 +137,8 @@ LL | VS(isize, <&'static [f64] as Deref>::Target), = help: the trait `std::marker::Sized` is not implemented for `[f64]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `[i32]: std::marker::Sized` is not satisfied - --> $DIR/unsized-enum2.rs:62:18 +error[E0277]: `[i32]` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:79:18 | LL | VT{u: isize, x: <&'static [i32] as Deref>::Target}, | ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ `[i32]` does not have a constant size known at compile-time @@ -146,40 +146,40 @@ LL | VT{u: isize, x: <&'static [i32] as Deref>::Target}, = help: the trait `std::marker::Sized` is not implemented for `[i32]` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `PathHelper1 + 'static: std::marker::Sized` is not satisfied in `Path1` - --> $DIR/unsized-enum2.rs:45:8 +error[E0277]: `PathHelper1 + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:53:8 | -LL | VI(Path1), //~ ERROR `PathHelper1 + 'static: std::marker::Sized` is not satisfied +LL | VI(Path1), | ^^^^^ `PathHelper1 + 'static` does not have a constant size known at compile-time | = help: within `Path1`, the trait `std::marker::Sized` is not implemented for `PathHelper1 + 'static` = note: required because it appears within the type `Path1` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `PathHelper2 + 'static: std::marker::Sized` is not satisfied in `Path2` - --> $DIR/unsized-enum2.rs:46:8 +error[E0277]: `PathHelper2 + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:55:8 | -LL | VJ{x: Path2}, //~ ERROR `PathHelper2 + 'static: std::marker::Sized` is not satisfied +LL | VJ{x: Path2}, | ^^^^^^^^ `PathHelper2 + 'static` does not have a constant size known at compile-time | = help: within `Path2`, the trait `std::marker::Sized` is not implemented for `PathHelper2 + 'static` = note: required because it appears within the type `Path2` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `PathHelper3 + 'static: std::marker::Sized` is not satisfied in `Path3` - --> $DIR/unsized-enum2.rs:47:15 +error[E0277]: `PathHelper3 + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:57:15 | -LL | VK(isize, Path3), //~ ERROR `PathHelper3 + 'static: std::marker::Sized` is not satisfied +LL | VK(isize, Path3), | ^^^^^ `PathHelper3 + 'static` does not have a constant size known at compile-time | = help: within `Path3`, the trait `std::marker::Sized` is not implemented for `PathHelper3 + 'static` = note: required because it appears within the type `Path3` = note: no field of an enum variant may have a dynamically sized type -error[E0277]: the trait bound `PathHelper4 + 'static: std::marker::Sized` is not satisfied in `Path4` - --> $DIR/unsized-enum2.rs:48:18 +error[E0277]: `PathHelper4 + 'static` does not have a constant size known at compile-time + --> $DIR/unsized-enum2.rs:59:18 | -LL | VL{u: isize, x: Path4}, //~ ERROR `PathHelper4 + 'static: std::marker::Sized` is not satisfied +LL | VL{u: isize, x: Path4}, | ^^^^^^^^ `PathHelper4 + 'static` does not have a constant size known at compile-time | = help: within `Path4`, the trait `std::marker::Sized` is not implemented for `PathHelper4 + 'static` -- cgit 1.4.1-3-g733a5 From 7c316090ebf216308b36ba1651bc6286b113ebd7 Mon Sep 17 00:00:00 2001 From: newpavlov Date: Tue, 26 Jun 2018 13:34:42 +0300 Subject: Deprecation of str::slice_uncheked(_mut) --- src/liballoc/str.rs | 8 ++++---- src/liballoc/string.rs | 2 +- src/libcore/str/mod.rs | 28 +++++++++++++++------------- src/libcore/str/pattern.rs | 2 +- 4 files changed, 21 insertions(+), 19 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 32ca8d1fa5e..2184a84ac7d 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -266,11 +266,11 @@ impl str { let mut result = String::new(); let mut last_end = 0; for (start, part) in self.match_indices(from) { - result.push_str(unsafe { self.slice_unchecked(last_end, start) }); + result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } - result.push_str(unsafe { self.slice_unchecked(last_end, self.len()) }); + result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } @@ -307,11 +307,11 @@ impl str { let mut result = String::with_capacity(32); let mut last_end = 0; for (start, part) in self.match_indices(pat).take(count) { - result.push_str(unsafe { self.slice_unchecked(last_end, start) }); + result.push_str(unsafe { self.get_unchecked(last_end..start) }); result.push_str(to); last_end = start + part.len(); } - result.push_str(unsafe { self.slice_unchecked(last_end, self.len()) }); + result.push_str(unsafe { self.get_unchecked(last_end..self.len()) }); result } diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index a988b6a26d9..7dbd86c0038 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -1222,7 +1222,7 @@ impl String { while idx < len { let ch = unsafe { - self.slice_unchecked(idx, len).chars().next().unwrap() + self.get_unchecked(idx..len).chars().next().unwrap() }; let ch_len = ch.len_utf8(); diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 5e1a9c25a21..210fc5fd5a6 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -1055,7 +1055,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { if !self.finished && (self.allow_trailing_empty || self.end - self.start > 0) { self.finished = true; unsafe { - let string = self.matcher.haystack().slice_unchecked(self.start, self.end); + let string = self.matcher.haystack().get_unchecked(self.start..self.end); Some(string) } } else { @@ -1070,7 +1070,7 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { let haystack = self.matcher.haystack(); match self.matcher.next_match() { Some((a, b)) => unsafe { - let elt = haystack.slice_unchecked(self.start, a); + let elt = haystack.get_unchecked(self.start..a); self.start = b; Some(elt) }, @@ -1095,13 +1095,13 @@ impl<'a, P: Pattern<'a>> SplitInternal<'a, P> { let haystack = self.matcher.haystack(); match self.matcher.next_match_back() { Some((a, b)) => unsafe { - let elt = haystack.slice_unchecked(b, self.end); + let elt = haystack.get_unchecked(b..self.end); self.end = a; Some(elt) }, None => unsafe { self.finished = true; - Some(haystack.slice_unchecked(self.start, self.end)) + Some(haystack.get_unchecked(self.start..self.end)) }, } } @@ -1222,7 +1222,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { #[inline] fn next(&mut self) -> Option<(usize, &'a str)> { self.0.next_match().map(|(start, end)| unsafe { - (start, self.0.haystack().slice_unchecked(start, end)) + (start, self.0.haystack().get_unchecked(start..end)) }) } @@ -1231,7 +1231,7 @@ impl<'a, P: Pattern<'a>> MatchIndicesInternal<'a, P> { where P::Searcher: ReverseSearcher<'a> { self.0.next_match_back().map(|(start, end)| unsafe { - (start, self.0.haystack().slice_unchecked(start, end)) + (start, self.0.haystack().get_unchecked(start..end)) }) } } @@ -1274,7 +1274,7 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { fn next(&mut self) -> Option<&'a str> { self.0.next_match().map(|(a, b)| unsafe { // Indices are known to be on utf8 boundaries - self.0.haystack().slice_unchecked(a, b) + self.0.haystack().get_unchecked(a..b) }) } @@ -1284,7 +1284,7 @@ impl<'a, P: Pattern<'a>> MatchesInternal<'a, P> { { self.0.next_match_back().map(|(a, b)| unsafe { // Indices are known to be on utf8 boundaries - self.0.haystack().slice_unchecked(a, b) + self.0.haystack().get_unchecked(a..b) }) } } @@ -2453,6 +2453,7 @@ impl str { /// } /// ``` #[stable(feature = "rust1", since = "1.0.0")] + #[rustc_deprecated(since = "1.28.0", reason = "duplicates `get_unchecked`")] #[inline] pub unsafe fn slice_unchecked(&self, begin: usize, end: usize) -> &str { (begin..end).get_unchecked(self) @@ -2483,6 +2484,7 @@ impl str { /// * `begin` and `end` must be byte positions within the string slice. /// * `begin` and `end` must lie on UTF-8 sequence boundaries. #[stable(feature = "str_slice_mut", since = "1.5.0")] + #[rustc_deprecated(since = "1.28.0", reason = "duplicates `get_unchecked`")] #[inline] pub unsafe fn slice_mut_unchecked(&mut self, begin: usize, end: usize) -> &mut str { (begin..end).get_unchecked_mut(self) @@ -2524,8 +2526,8 @@ impl str { // is_char_boundary checks that the index is in [0, .len()] if self.is_char_boundary(mid) { unsafe { - (self.slice_unchecked(0, mid), - self.slice_unchecked(mid, self.len())) + (self.get_unchecked(0..mid), + self.get_unchecked(mid..self.len())) } } else { slice_error_fail(self, 0, mid) @@ -3652,7 +3654,7 @@ impl str { } unsafe { // Searcher is known to return valid indices - self.slice_unchecked(i, j) + self.get_unchecked(i..j) } } @@ -3691,7 +3693,7 @@ impl str { } unsafe { // Searcher is known to return valid indices - self.slice_unchecked(i, self.len()) + self.get_unchecked(i..self.len()) } } @@ -3738,7 +3740,7 @@ impl str { } unsafe { // Searcher is known to return valid indices - self.slice_unchecked(0, j) + self.get_unchecked(0..j) } } diff --git a/src/libcore/str/pattern.rs b/src/libcore/str/pattern.rs index 464d57a2702..5e63fa9ff35 100644 --- a/src/libcore/str/pattern.rs +++ b/src/libcore/str/pattern.rs @@ -354,7 +354,7 @@ unsafe impl<'a> ReverseSearcher<'a> for CharSearcher<'a> { #[inline] fn next_back(&mut self) -> SearchStep { let old_finger = self.finger_back; - let slice = unsafe { self.haystack.slice_unchecked(self.finger, old_finger) }; + let slice = unsafe { self.haystack.get_unchecked(self.finger..old_finger) }; let mut iter = slice.chars(); let old_len = iter.iter.len(); if let Some(ch) = iter.next_back() { -- cgit 1.4.1-3-g733a5 From aaf2004f78d84e501c92b3a159de61819bd3db25 Mon Sep 17 00:00:00 2001 From: newpavlov Date: Tue, 26 Jun 2018 15:33:57 +0300 Subject: removed slice_uncheked from src/liballoc/tests/str.rs --- src/liballoc/tests/str.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/str.rs b/src/liballoc/tests/str.rs index 75306ac82df..6275c7bb112 100644 --- a/src/liballoc/tests/str.rs +++ b/src/liballoc/tests/str.rs @@ -177,9 +177,9 @@ fn test_join_for_different_lengths_with_long_separator() { #[test] fn test_unsafe_slice() { - assert_eq!("ab", unsafe {"abc".slice_unchecked(0, 2)}); - assert_eq!("bc", unsafe {"abc".slice_unchecked(1, 3)}); - assert_eq!("", unsafe {"abc".slice_unchecked(1, 1)}); + assert_eq!("ab", unsafe {"abc".get_unchecked(0..2)}); + assert_eq!("bc", unsafe {"abc".get_unchecked(1..3)}); + assert_eq!("", unsafe {"abc".get_unchecked(1..1)}); fn a_million_letter_a() -> String { let mut i = 0; let mut rs = String::new(); @@ -200,7 +200,7 @@ fn test_unsafe_slice() { } let letters = a_million_letter_a(); assert_eq!(half_a_million_letter_a(), - unsafe { letters.slice_unchecked(0, 500000)}); + unsafe { letters.get_unchecked(0..500000)}); } #[test] -- cgit 1.4.1-3-g733a5 From 433e6b31a75eea5ce45493acc63eae462d740338 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Tue, 26 Jun 2018 17:06:20 +0200 Subject: Add `LocalTaskObj` --- src/liballoc/boxed.rs | 18 +++++++++-- src/libcore/task/mod.rs | 4 +-- src/libcore/task/spawn_error.rs | 33 ++++++++++++++++++- src/libcore/task/task.rs | 71 +++++++++++++++++++++++++++++++++++++++-- 4 files changed, 118 insertions(+), 8 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index ea60c7775af..6a05ef68088 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -66,7 +66,7 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll, UnsafeTask, TaskObj}; +use core::task::{Context, Poll, UnsafeTask, TaskObj, LocalTaskObj}; use core::convert::From; use raw_vec::RawVec; @@ -933,7 +933,7 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + Send + 'static> UnsafeTask for PinBox { +unsafe impl + 'static> UnsafeTask for PinBox { fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } @@ -962,3 +962,17 @@ impl + Send + 'static> From> for TaskObj { TaskObj::new(PinBox::from(boxed)) } } + +#[unstable(feature = "futures_api", issue = "50547")] +impl + 'static> From> for LocalTaskObj { + fn from(boxed: PinBox) -> Self { + LocalTaskObj::new(boxed) + } +} + +#[unstable(feature = "futures_api", issue = "50547")] +impl + 'static> From> for LocalTaskObj { + fn from(boxed: Box) -> Self { + LocalTaskObj::new(PinBox::from(boxed)) + } +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 66ab21d177d..36370b6b37c 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -24,10 +24,10 @@ mod poll; pub use self::poll::Poll; mod spawn_error; -pub use self::spawn_error::{SpawnErrorKind, SpawnObjError}; +pub use self::spawn_error::{SpawnErrorKind, SpawnObjError, SpawnLocalObjError}; mod task; -pub use self::task::{TaskObj, UnsafeTask}; +pub use self::task::{TaskObj, LocalTaskObj, UnsafeTask}; mod wake; pub use self::wake::{Waker, LocalWaker, UnsafeWake}; diff --git a/src/libcore/task/spawn_error.rs b/src/libcore/task/spawn_error.rs index 5dd9c5be689..57bb9ebeb30 100644 --- a/src/libcore/task/spawn_error.rs +++ b/src/libcore/task/spawn_error.rs @@ -13,7 +13,8 @@ issue = "50547")] use fmt; -use super::TaskObj; +use mem; +use super::{TaskObj, LocalTaskObj}; /// Provides the reason that an executor was unable to spawn. pub struct SpawnErrorKind { @@ -49,3 +50,33 @@ pub struct SpawnObjError { /// The task for which spawning was attempted pub task: TaskObj, } + +/// The result of a failed spawn +#[derive(Debug)] +pub struct SpawnLocalObjError { + /// The kind of error + pub kind: SpawnErrorKind, + + /// The task for which spawning was attempted + pub task: LocalTaskObj, +} + +impl SpawnLocalObjError { + /// Converts the `SpawnLocalObjError` into a `SpawnObjError` + /// To make this operation safe one has to ensure that the `UnsafeTask` + /// instance from which the `LocalTaskObj` stored inside was created + /// actually implements `Send`. + pub unsafe fn as_spawn_obj_error(self) -> SpawnObjError { + // Safety: Both structs have the same memory layout + mem::transmute::(self) + } +} + +impl From for SpawnLocalObjError { + fn from(error: SpawnObjError) -> SpawnLocalObjError { + unsafe { + // Safety: Both structs have the same memory layout + mem::transmute::(error) + } + } +} diff --git a/src/libcore/task/task.rs b/src/libcore/task/task.rs index dc4ff314e5b..9896d7f5ff2 100644 --- a/src/libcore/task/task.rs +++ b/src/libcore/task/task.rs @@ -14,7 +14,7 @@ use fmt; use future::Future; -use mem::PinMut; +use mem::{self, PinMut}; use super::{Context, Poll}; /// A custom trait object for polling tasks, roughly akin to @@ -30,7 +30,7 @@ unsafe impl Send for TaskObj {} impl TaskObj { /// Create a `TaskObj` from a custom trait object representation. #[inline] - pub fn new(t: T) -> TaskObj { + pub fn new(t: T) -> TaskObj { TaskObj { ptr: t.into_raw(), poll_fn: T::poll, @@ -65,6 +65,71 @@ impl Drop for TaskObj { } } +/// A custom trait object for polling tasks, roughly akin to +/// `Box>`. +/// Contrary to `TaskObj`, `LocalTaskObj` does not have a `Send` bound. +pub struct LocalTaskObj { + ptr: *mut (), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, + drop_fn: unsafe fn(*mut ()), +} + +impl LocalTaskObj { + /// Create a `LocalTaskObj` from a custom trait object representation. + #[inline] + pub fn new(t: T) -> LocalTaskObj { + LocalTaskObj { + ptr: t.into_raw(), + poll_fn: T::poll, + drop_fn: T::drop, + } + } + + /// Converts the `LocalTaskObj` into a `TaskObj` + /// To make this operation safe one has to ensure that the `UnsafeTask` + /// instance from which this `LocalTaskObj` was created actually implements + /// `Send`. + pub unsafe fn as_task_obj(self) -> TaskObj { + // Safety: Both structs have the same memory layout + mem::transmute::(self) + } +} + +impl fmt::Debug for LocalTaskObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LocalTaskObj") + .finish() + } +} + +impl From for LocalTaskObj { + fn from(task: TaskObj) -> LocalTaskObj { + unsafe { + // Safety: Both structs have the same memory layout + mem::transmute::(task) + } + } +} + +impl Future for LocalTaskObj { + type Output = (); + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { + unsafe { + (self.poll_fn)(self.ptr, cx) + } + } +} + +impl Drop for LocalTaskObj { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.ptr) + } + } +} + /// A custom implementation of a task trait object for `TaskObj`, providing /// a hand-rolled vtable. /// @@ -74,7 +139,7 @@ impl Drop for TaskObj { /// The implementor must guarantee that it is safe to call `poll` repeatedly (in /// a non-concurrent fashion) with the result of `into_raw` until `drop` is /// called. -pub unsafe trait UnsafeTask: Send + 'static { +pub unsafe trait UnsafeTask: 'static { /// Convert a owned instance into a (conceptually owned) void pointer. fn into_raw(self) -> *mut (); -- cgit 1.4.1-3-g733a5 From b5cee029a55cd35fcdad52acb294a04ebff1f341 Mon Sep 17 00:00:00 2001 From: Clar Charr Date: Sat, 5 May 2018 00:33:20 -0400 Subject: Add str::split_ascii_whitespace. --- src/liballoc/lib.rs | 1 + src/liballoc/str.rs | 2 + src/libcore/str/mod.rs | 159 +++++++++++++++++++++++++++++++++++++++++++++++-- 3 files changed, 158 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index e25742a4a61..ec9b5eba561 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -108,6 +108,7 @@ #![cfg_attr(stage0, feature(repr_transparent))] #![feature(rustc_attrs)] #![feature(specialization)] +#![feature(split_ascii_whitespace)] #![feature(staged_api)] #![feature(str_internals)] #![feature(trusted_len)] diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index 32ca8d1fa5e..ec9c39c916c 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -78,6 +78,8 @@ pub use core::str::SplitWhitespace; pub use core::str::pattern; #[stable(feature = "encode_utf16", since = "1.8.0")] pub use core::str::EncodeUtf16; +#[unstable(feature = "split_ascii_whitespace", issue = "48656")] +pub use core::str::SplitAsciiWhitespace; #[unstable(feature = "slice_concat_ext", reason = "trait should not have to exist", diff --git a/src/libcore/str/mod.rs b/src/libcore/str/mod.rs index 42fb1bc238b..5ae2f6349e5 100644 --- a/src/libcore/str/mod.rs +++ b/src/libcore/str/mod.rs @@ -21,7 +21,7 @@ use char; use fmt; use iter::{Map, Cloned, FusedIterator, TrustedLen, Filter}; use iter_private::TrustedRandomAccess; -use slice::{self, SliceIndex}; +use slice::{self, SliceIndex, Split as SliceSplit}; use mem; pub mod pattern; @@ -2722,7 +2722,10 @@ impl str { /// the original string slice, separated by any amount of whitespace. /// /// 'Whitespace' is defined according to the terms of the Unicode Derived - /// Core Property `White_Space`. + /// Core Property `White_Space`. If you only want to split on ASCII whitespace + /// instead, use [`split_ascii_whitespace`]. + /// + /// [`split_ascii_whitespace`]: #method.split_ascii_whitespace /// /// # Examples /// @@ -2756,6 +2759,53 @@ impl str { SplitWhitespace { inner: self.split(IsWhitespace).filter(IsNotEmpty) } } + /// Split a string slice by ASCII whitespace. + /// + /// The iterator returned will return string slices that are sub-slices of + /// the original string slice, separated by any amount of ASCII whitespace. + /// + /// To split by Unicode `Whitespace` instead, use [`split_whitespace`]. + /// + /// [`split_whitespace`]: #method.split_whitespace + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// #![feature(split_ascii_whitespace)] + /// let mut iter = "A few words".split_ascii_whitespace(); + /// + /// assert_eq!(Some("A"), iter.next()); + /// assert_eq!(Some("few"), iter.next()); + /// assert_eq!(Some("words"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + /// + /// All kinds of ASCII whitespace are considered: + /// + /// ``` + /// let mut iter = " Mary had\ta little \n\t lamb".split_whitespace(); + /// assert_eq!(Some("Mary"), iter.next()); + /// assert_eq!(Some("had"), iter.next()); + /// assert_eq!(Some("a"), iter.next()); + /// assert_eq!(Some("little"), iter.next()); + /// assert_eq!(Some("lamb"), iter.next()); + /// + /// assert_eq!(None, iter.next()); + /// ``` + #[unstable(feature = "split_ascii_whitespace", issue = "48656")] + #[inline] + pub fn split_ascii_whitespace(&self) -> SplitAsciiWhitespace { + let inner = self + .as_bytes() + .split(IsAsciiWhitespace) + .filter(IsNotEmpty) + .map(UnsafeBytesToStr); + SplitAsciiWhitespace { inner } + } + /// An iterator over the lines of a string, as string slices. /// /// Lines are ended with either a newline (`\n`) or a carriage return with @@ -3895,6 +3945,20 @@ pub struct SplitWhitespace<'a> { inner: Filter, IsNotEmpty>, } +/// An iterator over the non-ASCII-whitespace substrings of a string, +/// separated by any amount of ASCII whitespace. +/// +/// This struct is created by the [`split_ascii_whitespace`] method on [`str`]. +/// See its documentation for more. +/// +/// [`split_ascii_whitespace`]: ../../std/primitive.str.html#method.split_ascii_whitespace +/// [`str`]: ../../std/primitive.str.html +#[unstable(feature = "split_ascii_whitespace", issue = "48656")] +#[derive(Clone, Debug)] +pub struct SplitAsciiWhitespace<'a> { + inner: Map, IsNotEmpty>, UnsafeBytesToStr>, +} + #[derive(Clone)] struct IsWhitespace; @@ -3914,6 +3978,25 @@ impl FnMut<(char, )> for IsWhitespace { } } +#[derive(Clone)] +struct IsAsciiWhitespace; + +impl<'a> FnOnce<(&'a u8, )> for IsAsciiWhitespace { + type Output = bool; + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (&u8, )) -> bool { + self.call_mut(arg) + } +} + +impl<'a> FnMut<(&'a u8, )> for IsAsciiWhitespace { + #[inline] + extern "rust-call" fn call_mut(&mut self, arg: (&u8, )) -> bool { + arg.0.is_ascii_whitespace() + } +} + #[derive(Clone)] struct IsNotEmpty; @@ -3921,30 +4004,72 @@ impl<'a, 'b> FnOnce<(&'a &'b str, )> for IsNotEmpty { type Output = bool; #[inline] - extern "rust-call" fn call_once(mut self, arg: (&&str, )) -> bool { + extern "rust-call" fn call_once(mut self, arg: (&'a &'b str, )) -> bool { self.call_mut(arg) } } impl<'a, 'b> FnMut<(&'a &'b str, )> for IsNotEmpty { #[inline] - extern "rust-call" fn call_mut(&mut self, arg: (&&str, )) -> bool { + extern "rust-call" fn call_mut(&mut self, arg: (&'a &'b str, )) -> bool { + !arg.0.is_empty() + } +} + +impl<'a, 'b> FnOnce<(&'a &'b [u8], )> for IsNotEmpty { + type Output = bool; + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (&'a &'b [u8], )) -> bool { + self.call_mut(arg) + } +} + +impl<'a, 'b> FnMut<(&'a &'b [u8], )> for IsNotEmpty { + #[inline] + extern "rust-call" fn call_mut(&mut self, arg: (&'a &'b [u8], )) -> bool { !arg.0.is_empty() } } +#[derive(Clone)] +struct UnsafeBytesToStr; + +impl<'a> FnOnce<(&'a [u8], )> for UnsafeBytesToStr { + type Output = &'a str; + + #[inline] + extern "rust-call" fn call_once(mut self, arg: (&'a [u8], )) -> &'a str { + self.call_mut(arg) + } +} + +impl<'a> FnMut<(&'a [u8], )> for UnsafeBytesToStr { + #[inline] + extern "rust-call" fn call_mut(&mut self, arg: (&'a [u8], )) -> &'a str { + unsafe { from_utf8_unchecked(arg.0) } + } +} + #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> Iterator for SplitWhitespace<'a> { type Item = &'a str; + #[inline] fn next(&mut self) -> Option<&'a str> { self.inner.next() } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } } #[stable(feature = "split_whitespace", since = "1.1.0")] impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { + #[inline] fn next_back(&mut self) -> Option<&'a str> { self.inner.next_back() } @@ -3953,6 +4078,32 @@ impl<'a> DoubleEndedIterator for SplitWhitespace<'a> { #[stable(feature = "fused", since = "1.26.0")] impl<'a> FusedIterator for SplitWhitespace<'a> {} +#[unstable(feature = "split_ascii_whitespace", issue = "48656")] +impl<'a> Iterator for SplitAsciiWhitespace<'a> { + type Item = &'a str; + + #[inline] + fn next(&mut self) -> Option<&'a str> { + self.inner.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[unstable(feature = "split_ascii_whitespace", issue = "48656")] +impl<'a> DoubleEndedIterator for SplitAsciiWhitespace<'a> { + #[inline] + fn next_back(&mut self) -> Option<&'a str> { + self.inner.next_back() + } +} + +#[unstable(feature = "split_ascii_whitespace", issue = "48656")] +impl<'a> FusedIterator for SplitAsciiWhitespace<'a> {} + /// An iterator of [`u16`] over the string encoded as UTF-16. /// /// [`u16`]: ../../std/primitive.u16.html -- cgit 1.4.1-3-g733a5 From 24ce2597823640726aa302a12a4de787a1fe1f05 Mon Sep 17 00:00:00 2001 From: Sean McArthur Date: Thu, 28 Jun 2018 11:49:47 -0700 Subject: Arc: remove unused allocation from Weak::new() --- src/liballoc/arc.rs | 56 ++++++++++++++++++++++++++++++++++------------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs index 0fbd1408f64..2abd9c85c57 100644 --- a/src/liballoc/arc.rs +++ b/src/liballoc/arc.rs @@ -23,7 +23,7 @@ use core::borrow; use core::fmt; use core::cmp::Ordering; use core::intrinsics::abort; -use core::mem::{self, align_of_val, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, size_of_val}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; @@ -43,6 +43,9 @@ use vec::Vec; /// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. const MAX_REFCOUNT: usize = (isize::MAX) as usize; +/// A sentinel value that is used for the pointer of `Weak::new()`. +const WEAK_EMPTY: usize = 1; + /// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically /// Reference Counted'. /// @@ -235,6 +238,10 @@ impl, U: ?Sized> CoerceUnsized> for Arc {} /// [`None`]: ../../std/option/enum.Option.html#variant.None #[stable(feature = "arc_weak", since = "1.4.0")] pub struct Weak { + // This is a `NonNull` to allow optimizing the size of this type in enums, + // but it is actually not truly "non-null". A `Weak::new()` will set this + // to a sentinel value, instead of needing to allocate some space in the + // heap. ptr: NonNull>, } @@ -1011,8 +1018,8 @@ impl Arc { } impl Weak { - /// Constructs a new `Weak`, allocating memory for `T` without initializing - /// it. Calling [`upgrade`] on the return value always gives [`None`]. + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. /// /// [`upgrade`]: struct.Weak.html#method.upgrade /// [`None`]: ../../std/option/enum.Option.html#variant.None @@ -1029,11 +1036,7 @@ impl Weak { pub fn new() -> Weak { unsafe { Weak { - ptr: Box::into_raw_non_null(box ArcInner { - strong: atomic::AtomicUsize::new(0), - weak: atomic::AtomicUsize::new(1), - data: uninitialized(), - }), + ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _), } } } @@ -1070,7 +1073,11 @@ impl Weak { pub fn upgrade(&self) -> Option> { // We use a CAS loop to increment the strong count instead of a // fetch_add because once the count hits 0 it must never be above 0. - let inner = self.inner(); + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return None; + } else { + unsafe { self.ptr.as_ref() } + }; // Relaxed load because any write of 0 that we can observe // leaves the field in a permanently zero state (so a @@ -1092,17 +1099,15 @@ impl Weak { // Relaxed is valid for the same reason it is on Arc's Clone impl match inner.strong.compare_exchange_weak(n, n + 1, Relaxed, Relaxed) { - Ok(_) => return Some(Arc { ptr: self.ptr, phantom: PhantomData }), + Ok(_) => return Some(Arc { + // null checked above + ptr: self.ptr, + phantom: PhantomData, + }), Err(old) => n = old, } } } - - #[inline] - fn inner(&self) -> &ArcInner { - // See comments above for why this is "safe" - unsafe { self.ptr.as_ref() } - } } #[stable(feature = "arc_weak", since = "1.4.0")] @@ -1120,11 +1125,16 @@ impl Clone for Weak { /// ``` #[inline] fn clone(&self) -> Weak { + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return Weak { ptr: self.ptr }; + } else { + unsafe { self.ptr.as_ref() } + }; // See comments in Arc::clone() for why this is relaxed. This can use a // fetch_add (ignoring the lock) because the weak count is only locked // where are *no other* weak pointers in existence. (So we can't be // running this code in that case). - let old_size = self.inner().weak.fetch_add(1, Relaxed); + let old_size = inner.weak.fetch_add(1, Relaxed); // See comments in Arc::clone() for why we do this (for mem::forget). if old_size > MAX_REFCOUNT { @@ -1139,8 +1149,8 @@ impl Clone for Weak { #[stable(feature = "downgraded_weak", since = "1.10.0")] impl Default for Weak { - /// Constructs a new `Weak`, allocating memory for `T` without initializing - /// it. Calling [`upgrade`] on the return value always gives [`None`]. + /// Constructs a new `Weak`, without allocating memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. /// /// [`upgrade`]: struct.Weak.html#method.upgrade /// [`None`]: ../../std/option/enum.Option.html#variant.None @@ -1193,7 +1203,13 @@ impl Drop for Weak { // weak count can only be locked if there was precisely one weak ref, // meaning that drop could only subsequently run ON that remaining weak // ref, which can only happen after the lock is released. - if self.inner().weak.fetch_sub(1, Release) == 1 { + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return; + } else { + unsafe { self.ptr.as_ref() } + }; + + if inner.weak.fetch_sub(1, Release) == 1 { atomic::fence(Acquire); unsafe { Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) -- cgit 1.4.1-3-g733a5 From 1acbb0a9350560d951359cc359361b87992a6f2b Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 03:36:34 +0200 Subject: Make raw_vec perma-unstable and hidden --- src/liballoc/raw_vec.rs | 7 +++++-- src/libarena/lib.rs | 1 + src/test/rustdoc-js/struct-vec.js | 1 - 3 files changed, 6 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 2369ce648fd..5095bbe96cc 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -8,6 +8,9 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. +#![unstable(feature = "raw_vec_internals", reason = "implemention detail", issue = "0")] +#![doc(hidden)] + use core::cmp; use core::mem; use core::ops::Drop; @@ -264,7 +267,7 @@ impl RawVec { /// # Examples /// /// ``` - /// # #![feature(alloc)] + /// # #![feature(alloc, raw_vec_internals)] /// # extern crate alloc; /// # use std::ptr; /// # use alloc::raw_vec::RawVec; @@ -468,7 +471,7 @@ impl RawVec { /// # Examples /// /// ``` - /// # #![feature(alloc)] + /// # #![feature(alloc, raw_vec_internals)] /// # extern crate alloc; /// # use std::ptr; /// # use alloc::raw_vec::RawVec; diff --git a/src/libarena/lib.rs b/src/libarena/lib.rs index b6a81596d06..0f4a5d16e17 100644 --- a/src/libarena/lib.rs +++ b/src/libarena/lib.rs @@ -26,6 +26,7 @@ #![feature(alloc)] #![feature(core_intrinsics)] #![feature(dropck_eyepatch)] +#![feature(raw_vec_internals)] #![cfg_attr(test, feature(test))] #![allow(deprecated)] diff --git a/src/test/rustdoc-js/struct-vec.js b/src/test/rustdoc-js/struct-vec.js index a91bc2d0da2..3874e23a2a3 100644 --- a/src/test/rustdoc-js/struct-vec.js +++ b/src/test/rustdoc-js/struct-vec.js @@ -14,6 +14,5 @@ const EXPECTED = { 'others': [ { 'path': 'std::vec', 'name': 'Vec' }, { 'path': 'std::collections', 'name': 'VecDeque' }, - { 'path': 'alloc::raw_vec', 'name': 'RawVec' }, ], }; -- cgit 1.4.1-3-g733a5 From 26324d0abe1a8df228f8c758b870a38078549ddc Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 03:39:17 +0200 Subject: Remove the unstable alloc::allocator module reexport, deprecated since 1.27 --- src/liballoc/lib.rs | 7 ------- 1 file changed, 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ec9b5eba561..585e34f5851 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -141,13 +141,6 @@ extern crate rand; #[macro_use] mod macros; -#[rustc_deprecated(since = "1.27.0", reason = "use the heap module in core, alloc, or std instead")] -#[unstable(feature = "allocator_api", issue = "32838")] -/// Use the `alloc` module instead. -pub mod allocator { - pub use alloc::*; -} - // Heaps provided for low-level allocation strategies pub mod alloc; -- cgit 1.4.1-3-g733a5 From 121b57b87ae4b58082f38a450373636286a8d678 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 03:52:25 +0200 Subject: Move some alloc crate top-level items to a new alloc::collections module This matches std::collections --- src/liballoc/binary_heap.rs | 1196 ------------ src/liballoc/btree/map.rs | 2578 -------------------------- src/liballoc/btree/mod.rs | 23 - src/liballoc/btree/node.rs | 1622 ---------------- src/liballoc/btree/search.rs | 75 - src/liballoc/btree/set.rs | 1153 ------------ src/liballoc/collections/binary_heap.rs | 1196 ++++++++++++ src/liballoc/collections/btree/map.rs | 2578 ++++++++++++++++++++++++++ src/liballoc/collections/btree/mod.rs | 23 + src/liballoc/collections/btree/node.rs | 1622 ++++++++++++++++ src/liballoc/collections/btree/search.rs | 75 + src/liballoc/collections/btree/set.rs | 1153 ++++++++++++ src/liballoc/collections/linked_list.rs | 1486 +++++++++++++++ src/liballoc/collections/mod.rs | 59 + src/liballoc/collections/vec_deque.rs | 2970 ++++++++++++++++++++++++++++++ src/liballoc/lib.rs | 37 +- src/liballoc/linked_list.rs | 1486 --------------- src/liballoc/str.rs | 1 - src/liballoc/vec_deque.rs | 2970 ------------------------------ src/libstd/collections/mod.rs | 8 +- 20 files changed, 11167 insertions(+), 11144 deletions(-) delete mode 100644 src/liballoc/binary_heap.rs delete mode 100644 src/liballoc/btree/map.rs delete mode 100644 src/liballoc/btree/mod.rs delete mode 100644 src/liballoc/btree/node.rs delete mode 100644 src/liballoc/btree/search.rs delete mode 100644 src/liballoc/btree/set.rs create mode 100644 src/liballoc/collections/binary_heap.rs create mode 100644 src/liballoc/collections/btree/map.rs create mode 100644 src/liballoc/collections/btree/mod.rs create mode 100644 src/liballoc/collections/btree/node.rs create mode 100644 src/liballoc/collections/btree/search.rs create mode 100644 src/liballoc/collections/btree/set.rs create mode 100644 src/liballoc/collections/linked_list.rs create mode 100644 src/liballoc/collections/mod.rs create mode 100644 src/liballoc/collections/vec_deque.rs delete mode 100644 src/liballoc/linked_list.rs delete mode 100644 src/liballoc/vec_deque.rs (limited to 'src/liballoc') diff --git a/src/liballoc/binary_heap.rs b/src/liballoc/binary_heap.rs deleted file mode 100644 index fcadcb544c4..00000000000 --- a/src/liballoc/binary_heap.rs +++ /dev/null @@ -1,1196 +0,0 @@ -// Copyright 2013-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A priority queue implemented with a binary heap. -//! -//! Insertion and popping the largest element have `O(log n)` time complexity. -//! Checking the largest element is `O(1)`. Converting a vector to a binary heap -//! can be done in-place, and has `O(n)` complexity. A binary heap can also be -//! converted to a sorted vector in-place, allowing it to be used for an `O(n -//! log n)` in-place heapsort. -//! -//! # Examples -//! -//! This is a larger example that implements [Dijkstra's algorithm][dijkstra] -//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. -//! It shows how to use [`BinaryHeap`] with custom types. -//! -//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm -//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem -//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph -//! [`BinaryHeap`]: struct.BinaryHeap.html -//! -//! ``` -//! use std::cmp::Ordering; -//! use std::collections::BinaryHeap; -//! use std::usize; -//! -//! #[derive(Copy, Clone, Eq, PartialEq)] -//! struct State { -//! cost: usize, -//! position: usize, -//! } -//! -//! // The priority queue depends on `Ord`. -//! // Explicitly implement the trait so the queue becomes a min-heap -//! // instead of a max-heap. -//! impl Ord for State { -//! fn cmp(&self, other: &State) -> Ordering { -//! // Notice that the we flip the ordering on costs. -//! // In case of a tie we compare positions - this step is necessary -//! // to make implementations of `PartialEq` and `Ord` consistent. -//! other.cost.cmp(&self.cost) -//! .then_with(|| self.position.cmp(&other.position)) -//! } -//! } -//! -//! // `PartialOrd` needs to be implemented as well. -//! impl PartialOrd for State { -//! fn partial_cmp(&self, other: &State) -> Option { -//! Some(self.cmp(other)) -//! } -//! } -//! -//! // Each node is represented as an `usize`, for a shorter implementation. -//! struct Edge { -//! node: usize, -//! cost: usize, -//! } -//! -//! // Dijkstra's shortest path algorithm. -//! -//! // Start at `start` and use `dist` to track the current shortest distance -//! // to each node. This implementation isn't memory-efficient as it may leave duplicate -//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value, -//! // for a simpler implementation. -//! fn shortest_path(adj_list: &Vec>, start: usize, goal: usize) -> Option { -//! // dist[node] = current shortest distance from `start` to `node` -//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect(); -//! -//! let mut heap = BinaryHeap::new(); -//! -//! // We're at `start`, with a zero cost -//! dist[start] = 0; -//! heap.push(State { cost: 0, position: start }); -//! -//! // Examine the frontier with lower cost nodes first (min-heap) -//! while let Some(State { cost, position }) = heap.pop() { -//! // Alternatively we could have continued to find all shortest paths -//! if position == goal { return Some(cost); } -//! -//! // Important as we may have already found a better way -//! if cost > dist[position] { continue; } -//! -//! // For each node we can reach, see if we can find a way with -//! // a lower cost going through this node -//! for edge in &adj_list[position] { -//! let next = State { cost: cost + edge.cost, position: edge.node }; -//! -//! // If so, add it to the frontier and continue -//! if next.cost < dist[next.position] { -//! heap.push(next); -//! // Relaxation, we have now found a better way -//! dist[next.position] = next.cost; -//! } -//! } -//! } -//! -//! // Goal not reachable -//! None -//! } -//! -//! fn main() { -//! // This is the directed graph we're going to use. -//! // The node numbers correspond to the different states, -//! // and the edge weights symbolize the cost of moving -//! // from one node to another. -//! // Note that the edges are one-way. -//! // -//! // 7 -//! // +-----------------+ -//! // | | -//! // v 1 2 | 2 -//! // 0 -----> 1 -----> 3 ---> 4 -//! // | ^ ^ ^ -//! // | | 1 | | -//! // | | | 3 | 1 -//! // +------> 2 -------+ | -//! // 10 | | -//! // +---------------+ -//! // -//! // The graph is represented as an adjacency list where each index, -//! // corresponding to a node value, has a list of outgoing edges. -//! // Chosen for its efficiency. -//! let graph = vec![ -//! // Node 0 -//! vec![Edge { node: 2, cost: 10 }, -//! Edge { node: 1, cost: 1 }], -//! // Node 1 -//! vec![Edge { node: 3, cost: 2 }], -//! // Node 2 -//! vec![Edge { node: 1, cost: 1 }, -//! Edge { node: 3, cost: 3 }, -//! Edge { node: 4, cost: 1 }], -//! // Node 3 -//! vec![Edge { node: 0, cost: 7 }, -//! Edge { node: 4, cost: 2 }], -//! // Node 4 -//! vec![]]; -//! -//! assert_eq!(shortest_path(&graph, 0, 1), Some(1)); -//! assert_eq!(shortest_path(&graph, 0, 3), Some(3)); -//! assert_eq!(shortest_path(&graph, 3, 0), Some(7)); -//! assert_eq!(shortest_path(&graph, 0, 4), Some(5)); -//! assert_eq!(shortest_path(&graph, 4, 0), None); -//! } -//! ``` - -#![allow(missing_docs)] -#![stable(feature = "rust1", since = "1.0.0")] - -use core::ops::{Deref, DerefMut}; -use core::iter::{FromIterator, FusedIterator}; -use core::mem::{swap, size_of, ManuallyDrop}; -use core::ptr; -use core::fmt; - -use slice; -use vec::{self, Vec}; - -use super::SpecExtend; - -/// A priority queue implemented with a binary heap. -/// -/// This will be a max-heap. -/// -/// It is a logic error for an item to be modified in such a way that the -/// item's ordering relative to any other item, as determined by the `Ord` -/// trait, changes while it is in the heap. This is normally only possible -/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. -/// -/// # Examples -/// -/// ``` -/// use std::collections::BinaryHeap; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `BinaryHeap` in this example). -/// let mut heap = BinaryHeap::new(); -/// -/// // We can use peek to look at the next item in the heap. In this case, -/// // there's no items in there yet so we get None. -/// assert_eq!(heap.peek(), None); -/// -/// // Let's add some scores... -/// heap.push(1); -/// heap.push(5); -/// heap.push(2); -/// -/// // Now peek shows the most important item in the heap. -/// assert_eq!(heap.peek(), Some(&5)); -/// -/// // We can check the length of a heap. -/// assert_eq!(heap.len(), 3); -/// -/// // We can iterate over the items in the heap, although they are returned in -/// // a random order. -/// for x in &heap { -/// println!("{}", x); -/// } -/// -/// // If we instead pop these scores, they should come back in order. -/// assert_eq!(heap.pop(), Some(5)); -/// assert_eq!(heap.pop(), Some(2)); -/// assert_eq!(heap.pop(), Some(1)); -/// assert_eq!(heap.pop(), None); -/// -/// // We can clear the heap of any remaining items. -/// heap.clear(); -/// -/// // The heap should now be empty. -/// assert!(heap.is_empty()) -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BinaryHeap { - data: Vec, -} - -/// Structure wrapping a mutable reference to the greatest item on a -/// `BinaryHeap`. -/// -/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See -/// its documentation for more. -/// -/// [`peek_mut`]: struct.BinaryHeap.html#method.peek_mut -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -pub struct PeekMut<'a, T: 'a + Ord> { - heap: &'a mut BinaryHeap, - sift: bool, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: Ord + fmt::Debug> fmt::Debug for PeekMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("PeekMut") - .field(&self.heap.data[0]) - .finish() - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T: Ord> Drop for PeekMut<'a, T> { - fn drop(&mut self) { - if self.sift { - self.heap.sift_down(0); - } - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T: Ord> Deref for PeekMut<'a, T> { - type Target = T; - fn deref(&self) -> &T { - &self.heap.data[0] - } -} - -#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] -impl<'a, T: Ord> DerefMut for PeekMut<'a, T> { - fn deref_mut(&mut self) -> &mut T { - &mut self.heap.data[0] - } -} - -impl<'a, T: Ord> PeekMut<'a, T> { - /// Removes the peeked value from the heap and returns it. - #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")] - pub fn pop(mut this: PeekMut<'a, T>) -> T { - let value = this.heap.pop().unwrap(); - this.sift = false; - value - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BinaryHeap { - fn clone(&self) -> Self { - BinaryHeap { data: self.data.clone() } - } - - fn clone_from(&mut self, source: &Self) { - self.data.clone_from(&source.data); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BinaryHeap { - /// Creates an empty `BinaryHeap`. - #[inline] - fn default() -> BinaryHeap { - BinaryHeap::new() - } -} - -#[stable(feature = "binaryheap_debug", since = "1.4.0")] -impl fmt::Debug for BinaryHeap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.iter()).finish() - } -} - -impl BinaryHeap { - /// Creates an empty `BinaryHeap` as a max-heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BinaryHeap { - BinaryHeap { data: vec![] } - } - - /// Creates an empty `BinaryHeap` with a specific capacity. - /// This preallocates enough memory for `capacity` elements, - /// so that the `BinaryHeap` does not have to be reallocated - /// until it contains at least that many values. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity(10); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(capacity: usize) -> BinaryHeap { - BinaryHeap { data: Vec::with_capacity(capacity) } - } - - /// Returns an iterator visiting all values in the underlying vector, in - /// arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order - /// for x in heap.iter() { - /// println!("{}", x); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { iter: self.data.iter() } - } - - /// Returns the greatest item in the binary heap, or `None` if it is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// assert_eq!(heap.peek(), None); - /// - /// heap.push(1); - /// heap.push(5); - /// heap.push(2); - /// assert_eq!(heap.peek(), Some(&5)); - /// - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn peek(&self) -> Option<&T> { - self.data.get(0) - } - - /// Returns a mutable reference to the greatest item in the binary heap, or - /// `None` if it is empty. - /// - /// Note: If the `PeekMut` value is leaked, the heap may be in an - /// inconsistent state. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// assert!(heap.peek_mut().is_none()); - /// - /// heap.push(1); - /// heap.push(5); - /// heap.push(2); - /// { - /// let mut val = heap.peek_mut().unwrap(); - /// *val = 0; - /// } - /// assert_eq!(heap.peek(), Some(&2)); - /// ``` - #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] - pub fn peek_mut(&mut self) -> Option> { - if self.is_empty() { - None - } else { - Some(PeekMut { - heap: self, - sift: true, - }) - } - } - - /// Returns the number of elements the binary heap can hold without reallocating. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::with_capacity(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.data.capacity() - } - - /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the - /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it requests. Therefore - /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future - /// insertions are expected. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve_exact(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - /// - /// [`reserve`]: #method.reserve - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.data.reserve_exact(additional); - } - - /// Reserves capacity for at least `additional` more elements to be inserted in the - /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.reserve(100); - /// assert!(heap.capacity() >= 100); - /// heap.push(4); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - self.data.reserve(additional); - } - - /// Discards as much additional capacity as possible. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to_fit(); - /// assert!(heap.capacity() == 0); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn shrink_to_fit(&mut self) { - self.data.shrink_to_fit(); - } - - /// Discards capacity with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::collections::BinaryHeap; - /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); - /// - /// assert!(heap.capacity() >= 100); - /// heap.shrink_to(10); - /// assert!(heap.capacity() >= 10); - /// ``` - #[inline] - #[unstable(feature = "shrink_to", reason = "new API", issue="0")] - pub fn shrink_to(&mut self, min_capacity: usize) { - self.data.shrink_to(min_capacity) - } - - /// Removes the greatest item from the binary heap and returns it, or `None` if it - /// is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert_eq!(heap.pop(), Some(3)); - /// assert_eq!(heap.pop(), Some(1)); - /// assert_eq!(heap.pop(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop(&mut self) -> Option { - self.data.pop().map(|mut item| { - if !self.is_empty() { - swap(&mut item, &mut self.data[0]); - self.sift_down_to_bottom(0); - } - item - }) - } - - /// Pushes an item onto the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// heap.push(3); - /// heap.push(5); - /// heap.push(1); - /// - /// assert_eq!(heap.len(), 3); - /// assert_eq!(heap.peek(), Some(&5)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push(&mut self, item: T) { - let old_len = self.len(); - self.data.push(item); - self.sift_up(0, old_len); - } - - /// Consumes the `BinaryHeap` and returns the underlying vector - /// in arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); - /// let vec = heap.into_vec(); - /// - /// // Will print in some order - /// for x in vec { - /// println!("{}", x); - /// } - /// ``` - #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_vec(self) -> Vec { - self.into() - } - - /// Consumes the `BinaryHeap` and returns a vector in sorted - /// (ascending) order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// - /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); - /// heap.push(6); - /// heap.push(3); - /// - /// let vec = heap.into_sorted_vec(); - /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]); - /// ``` - #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] - pub fn into_sorted_vec(mut self) -> Vec { - let mut end = self.len(); - while end > 1 { - end -= 1; - self.data.swap(0, end); - self.sift_down_range(0, end); - } - self.into_vec() - } - - // The implementations of sift_up and sift_down use unsafe blocks in - // order to move an element out of the vector (leaving behind a - // hole), shift along the others and move the removed element back into the - // vector at the final location of the hole. - // The `Hole` type is used to represent this, and make sure - // the hole is filled back at the end of its scope, even on panic. - // Using a hole reduces the constant factor compared to using swaps, - // which involves twice as many moves. - fn sift_up(&mut self, start: usize, pos: usize) -> usize { - unsafe { - // Take out the value at `pos` and create a hole. - let mut hole = Hole::new(&mut self.data, pos); - - while hole.pos() > start { - let parent = (hole.pos() - 1) / 2; - if hole.element() <= hole.get(parent) { - break; - } - hole.move_to(parent); - } - hole.pos() - } - } - - /// Take an element at `pos` and move it down the heap, - /// while its children are larger. - fn sift_down_range(&mut self, pos: usize, end: usize) { - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && !(hole.get(child) > hole.get(right)) { - child = right; - } - // if we are already in order, stop. - if hole.element() >= hole.get(child) { - break; - } - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - } - } - - fn sift_down(&mut self, pos: usize) { - let len = self.len(); - self.sift_down_range(pos, len); - } - - /// Take an element at `pos` and move it all the way down the heap, - /// then sift it up to its position. - /// - /// Note: This is faster when the element is known to be large / should - /// be closer to the bottom. - fn sift_down_to_bottom(&mut self, mut pos: usize) { - let end = self.len(); - let start = pos; - unsafe { - let mut hole = Hole::new(&mut self.data, pos); - let mut child = 2 * pos + 1; - while child < end { - let right = child + 1; - // compare with the greater of the two children - if right < end && !(hole.get(child) > hole.get(right)) { - child = right; - } - hole.move_to(child); - child = 2 * hole.pos() + 1; - } - pos = hole.pos; - } - self.sift_up(start, pos); - } - - /// Returns the length of the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert_eq!(heap.len(), 2); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.data.len() - } - - /// Checks if the binary heap is empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::new(); - /// - /// assert!(heap.is_empty()); - /// - /// heap.push(3); - /// heap.push(5); - /// heap.push(1); - /// - /// assert!(!heap.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } - - /// Clears the binary heap, returning an iterator over the removed elements. - /// - /// The elements are removed in arbitrary order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// for x in heap.drain() { - /// println!("{}", x); - /// } - /// - /// assert!(heap.is_empty()); - /// ``` - #[inline] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self) -> Drain { - Drain { iter: self.data.drain(..) } - } - - /// Drops all items from the binary heap. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let mut heap = BinaryHeap::from(vec![1, 3]); - /// - /// assert!(!heap.is_empty()); - /// - /// heap.clear(); - /// - /// assert!(heap.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.drain(); - } - - fn rebuild(&mut self) { - let mut n = self.len() / 2; - while n > 0 { - n -= 1; - self.sift_down(n); - } - } - - /// Moves all the elements of `other` into `self`, leaving `other` empty. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// - /// let v = vec![-10, 1, 2, 3, 3]; - /// let mut a = BinaryHeap::from(v); - /// - /// let v = vec![-20, 5, 43]; - /// let mut b = BinaryHeap::from(v); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); - /// assert!(b.is_empty()); - /// ``` - #[stable(feature = "binary_heap_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - if self.len() < other.len() { - swap(self, other); - } - - if other.is_empty() { - return; - } - - #[inline(always)] - fn log2_fast(x: usize) -> usize { - 8 * size_of::() - (x.leading_zeros() as usize) - 1 - } - - // `rebuild` takes O(len1 + len2) operations - // and about 2 * (len1 + len2) comparisons in the worst case - // while `extend` takes O(len2 * log_2(len1)) operations - // and about 1 * len2 * log_2(len1) comparisons in the worst case, - // assuming len1 >= len2. - #[inline] - fn better_to_rebuild(len1: usize, len2: usize) -> bool { - 2 * (len1 + len2) < len2 * log2_fast(len1) - } - - if better_to_rebuild(self.len(), other.len()) { - self.data.append(&mut other.data); - self.rebuild(); - } else { - self.extend(other.drain()); - } - } -} - -/// Hole represents a hole in a slice i.e. an index without valid value -/// (because it was moved from or duplicated). -/// In drop, `Hole` will restore the slice by filling the hole -/// position with the value that was originally removed. -struct Hole<'a, T: 'a> { - data: &'a mut [T], - elt: ManuallyDrop, - pos: usize, -} - -impl<'a, T> Hole<'a, T> { - /// Create a new Hole at index `pos`. - /// - /// Unsafe because pos must be within the data slice. - #[inline] - unsafe fn new(data: &'a mut [T], pos: usize) -> Self { - debug_assert!(pos < data.len()); - let elt = ptr::read(&data[pos]); - Hole { - data, - elt: ManuallyDrop::new(elt), - pos, - } - } - - #[inline] - fn pos(&self) -> usize { - self.pos - } - - /// Returns a reference to the element removed. - #[inline] - fn element(&self) -> &T { - &self.elt - } - - /// Returns a reference to the element at `index`. - /// - /// Unsafe because index must be within the data slice and not equal to pos. - #[inline] - unsafe fn get(&self, index: usize) -> &T { - debug_assert!(index != self.pos); - debug_assert!(index < self.data.len()); - self.data.get_unchecked(index) - } - - /// Move hole to new location - /// - /// Unsafe because index must be within the data slice and not equal to pos. - #[inline] - unsafe fn move_to(&mut self, index: usize) { - debug_assert!(index != self.pos); - debug_assert!(index < self.data.len()); - let index_ptr: *const _ = self.data.get_unchecked(index); - let hole_ptr = self.data.get_unchecked_mut(self.pos); - ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); - self.pos = index; - } -} - -impl<'a, T> Drop for Hole<'a, T> { - #[inline] - fn drop(&mut self) { - // fill the hole again - unsafe { - let pos = self.pos; - ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); - } - } -} - -/// An iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`iter`] method on [`BinaryHeap`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.BinaryHeap.html#method.iter -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - iter: slice::Iter<'a, T>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Iter") - .field(&self.iter.as_slice()) - .finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Iter<'a, T> { - fn clone(&self) -> Iter<'a, T> { - Iter { iter: self.iter.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for Iter<'a, T> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for Iter<'a, T> {} - -/// An owning iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`][`BinaryHeap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.BinaryHeap.html#method.into_iter -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Clone)] -pub struct IntoIter { - iter: vec::IntoIter, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("IntoIter") - .field(&self.iter.as_slice()) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -/// A draining iterator over the elements of a `BinaryHeap`. -/// -/// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its -/// documentation for more. -/// -/// [`drain`]: struct.BinaryHeap.html#method.drain -/// [`BinaryHeap`]: struct.BinaryHeap.html -#[stable(feature = "drain", since = "1.6.0")] -#[derive(Debug)] -pub struct Drain<'a, T: 'a> { - iter: vec::Drain<'a, T>, -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> Iterator for Drain<'a, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> { - fn is_empty(&self) -> bool { - self.iter.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} - -#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for BinaryHeap { - fn from(vec: Vec) -> BinaryHeap { - let mut heap = BinaryHeap { data: vec }; - heap.rebuild(); - heap - } -} - -#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] -impl From> for Vec { - fn from(heap: BinaryHeap) -> Vec { - heap.data - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for BinaryHeap { - fn from_iter>(iter: I) -> BinaryHeap { - BinaryHeap::from(iter.into_iter().collect::>()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BinaryHeap { - type Item = T; - type IntoIter = IntoIter; - - /// Creates a consuming iterator, that is, one that moves each value out of - /// the binary heap in arbitrary order. The binary heap cannot be used - /// after calling this. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BinaryHeap; - /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); - /// - /// // Print 1, 2, 3, 4 in arbitrary order - /// for x in heap.into_iter() { - /// // x has type i32, not &i32 - /// println!("{}", x); - /// } - /// ``` - fn into_iter(self) -> IntoIter { - IntoIter { iter: self.data.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BinaryHeap - where T: Ord -{ - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for BinaryHeap { - #[inline] - fn extend>(&mut self, iter: I) { - >::spec_extend(self, iter); - } -} - -impl> SpecExtend for BinaryHeap { - default fn spec_extend(&mut self, iter: I) { - self.extend_desugared(iter.into_iter()); - } -} - -impl SpecExtend> for BinaryHeap { - fn spec_extend(&mut self, ref mut other: BinaryHeap) { - self.append(other); - } -} - -impl BinaryHeap { - fn extend_desugared>(&mut self, iter: I) { - let iterator = iter.into_iter(); - let (lower, _) = iterator.size_hint(); - - self.reserve(lower); - - for elem in iterator { - self.push(elem); - } - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } -} diff --git a/src/liballoc/btree/map.rs b/src/liballoc/btree/map.rs deleted file mode 100644 index e6e454446e2..00000000000 --- a/src/liballoc/btree/map.rs +++ /dev/null @@ -1,2578 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::cmp::Ordering; -use core::fmt::Debug; -use core::hash::{Hash, Hasher}; -use core::iter::{FromIterator, Peekable, FusedIterator}; -use core::marker::PhantomData; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::Index; -use core::ops::RangeBounds; -use core::{fmt, intrinsics, mem, ptr}; - -use borrow::Borrow; - -use super::node::{self, Handle, NodeRef, marker}; -use super::search; - -use super::node::InsertResult::*; -use super::node::ForceResult::*; -use super::search::SearchResult::*; -use self::UnderflowResult::*; -use self::Entry::*; - -/// A map based on a B-Tree. -/// -/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing -/// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal -/// choice for a sorted map, as a perfectly balanced BST performs the theoretical minimum amount of -/// comparisons necessary to find an element (log2n). However, in practice the way this -/// is done is *very* inefficient for modern computer architectures. In particular, every element -/// is stored in its own individually heap-allocated node. This means that every single insertion -/// triggers a heap-allocation, and every single comparison should be a cache-miss. Since these -/// are both notably expensive things to do in practice, we are forced to at very least reconsider -/// the BST strategy. -/// -/// A B-Tree instead makes each node contain B-1 to 2B-1 elements in a contiguous array. By doing -/// this, we reduce the number of allocations by a factor of B, and improve cache efficiency in -/// searches. However, this does mean that searches will have to do *more* comparisons on average. -/// The precise number of comparisons depends on the node search strategy used. For optimal cache -/// efficiency, one could search the nodes linearly. For optimal comparisons, one could search -/// the node using binary search. As a compromise, one could also perform a linear search -/// that initially only checks every ith element for some choice of i. -/// -/// Currently, our implementation simply performs naive linear search. This provides excellent -/// performance on *small* nodes of elements which are cheap to compare. However in the future we -/// would like to further explore choosing the optimal search strategy based on the choice of B, -/// and possibly other factors. Using linear search, searching for a random element is expected -/// to take O(B logBn) comparisons, which is generally worse than a BST. In practice, -/// however, performance is excellent. -/// -/// It is a logic error for a key to be modified in such a way that the key's ordering relative to -/// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// [`Ord`]: ../../std/cmp/trait.Ord.html -/// [`Cell`]: ../../std/cell/struct.Cell.html -/// [`RefCell`]: ../../std/cell/struct.RefCell.html -/// -/// # Examples -/// -/// ``` -/// use std::collections::BTreeMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `BTreeMap<&str, &str>` in this example). -/// let mut movie_reviews = BTreeMap::new(); -/// -/// // review some movies. -/// movie_reviews.insert("Office Space", "Deals with real issues in the workplace."); -/// movie_reviews.insert("Pulp Fiction", "Masterpiece."); -/// movie_reviews.insert("The Godfather", "Very enjoyable."); -/// movie_reviews.insert("The Blues Brothers", "Eye lyked it alot."); -/// -/// // check for a specific one. -/// if !movie_reviews.contains_key("Les Misérables") { -/// println!("We've got {} reviews, but Les Misérables ain't one.", -/// movie_reviews.len()); -/// } -/// -/// // oops, this review has a lot of spelling mistakes, let's delete it. -/// movie_reviews.remove("The Blues Brothers"); -/// -/// // look up the values associated with some keys. -/// let to_find = ["Up!", "Office Space"]; -/// for book in &to_find { -/// match movie_reviews.get(book) { -/// Some(review) => println!("{}: {}", book, review), -/// None => println!("{} is unreviewed.", book) -/// } -/// } -/// -/// // iterate over everything. -/// for (movie, review) in &movie_reviews { -/// println!("{}: \"{}\"", movie, review); -/// } -/// ``` -/// -/// `BTreeMap` also implements an [`Entry API`](#method.entry), which allows -/// for more complex methods of getting, setting, updating and removing keys and -/// their values: -/// -/// ``` -/// use std::collections::BTreeMap; -/// -/// // type inference lets us omit an explicit type signature (which -/// // would be `BTreeMap<&str, u8>` in this example). -/// let mut player_stats = BTreeMap::new(); -/// -/// fn random_stat_buff() -> u8 { -/// // could actually return some random value here - let's just return -/// // some fixed value for now -/// 42 -/// } -/// -/// // insert a key only if it doesn't already exist -/// player_stats.entry("health").or_insert(100); -/// -/// // insert a key using a function that provides a new value only if it -/// // doesn't already exist -/// player_stats.entry("defence").or_insert_with(random_stat_buff); -/// -/// // update a key, guarding against the key possibly not being set -/// let stat = player_stats.entry("attack").or_insert(100); -/// *stat += random_stat_buff(); -/// ``` -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BTreeMap { - root: node::Root, - length: usize, -} - -#[stable(feature = "btree_drop", since = "1.7.0")] -unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { - fn drop(&mut self) { - unsafe { - drop(ptr::read(self).into_iter()); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for BTreeMap { - fn clone(&self) -> BTreeMap { - fn clone_subtree(node: node::NodeRef) - -> BTreeMap { - - match node.force() { - Leaf(leaf) => { - let mut out_tree = BTreeMap { - root: node::Root::new_leaf(), - length: 0, - }; - - { - let mut out_node = match out_tree.root.as_mut().force() { - Leaf(leaf) => leaf, - Internal(_) => unreachable!(), - }; - - let mut in_edge = leaf.first_edge(); - while let Ok(kv) = in_edge.right_kv() { - let (k, v) = kv.into_kv(); - in_edge = kv.right_edge(); - - out_node.push(k.clone(), v.clone()); - out_tree.length += 1; - } - } - - out_tree - } - Internal(internal) => { - let mut out_tree = clone_subtree(internal.first_edge().descend()); - - { - let mut out_node = out_tree.root.push_level(); - let mut in_edge = internal.first_edge(); - while let Ok(kv) = in_edge.right_kv() { - let (k, v) = kv.into_kv(); - in_edge = kv.right_edge(); - - let k = (*k).clone(); - let v = (*v).clone(); - let subtree = clone_subtree(in_edge.descend()); - - // We can't destructure subtree directly - // because BTreeMap implements Drop - let (subroot, sublength) = unsafe { - let root = ptr::read(&subtree.root); - let length = subtree.length; - mem::forget(subtree); - (root, length) - }; - - out_node.push(k, v, subroot); - out_tree.length += 1 + sublength; - } - } - - out_tree - } - } - } - - clone_subtree(self.root.as_ref()) - } -} - -impl super::Recover for BTreeMap - where K: Borrow + Ord, - Q: Ord -{ - type Key = K; - - fn get(&self, key: &Q) -> Option<&K> { - match search::search_tree(self.root.as_ref(), key) { - Found(handle) => Some(handle.into_kv().0), - GoDown(_) => None, - } - } - - fn take(&mut self, key: &Q) -> Option { - match search::search_tree(self.root.as_mut(), key) { - Found(handle) => { - Some(OccupiedEntry { - handle, - length: &mut self.length, - _marker: PhantomData, - } - .remove_kv() - .0) - } - GoDown(_) => None, - } - } - - fn replace(&mut self, key: K) -> Option { - self.ensure_root_is_owned(); - match search::search_tree::(self.root.as_mut(), &key) { - Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), - GoDown(handle) => { - VacantEntry { - key, - handle, - length: &mut self.length, - _marker: PhantomData, - } - .insert(()); - None - } - } - } -} - -/// An iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`iter`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.BTreeMap.html#method.iter -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, K: 'a, V: 'a> { - range: Range<'a, K, V>, - length: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Iter<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: struct.BTreeMap.html#method.iter_mut -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct IterMut<'a, K: 'a, V: 'a> { - range: RangeMut<'a, K, V>, - length: usize, -} - -/// An owning iterator over the entries of a `BTreeMap`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`][`BTreeMap`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.BTreeMap.html#method.into_iter -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - front: Handle, marker::Edge>, - back: Handle, marker::Edge>, - length: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let range = Range { - front: self.front.reborrow(), - back: self.back.reborrow(), - }; - f.debug_list().entries(range).finish() - } -} - -/// An iterator over the keys of a `BTreeMap`. -/// -/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`keys`]: struct.BTreeMap.html#method.keys -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Keys<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, K: 'a + fmt::Debug, V: 'a> fmt::Debug for Keys<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// An iterator over the values of a `BTreeMap`. -/// -/// This `struct` is created by the [`values`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`values`]: struct.BTreeMap.html#method.values -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Values<'a, K: 'a, V: 'a> { - inner: Iter<'a, K, V>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, K: 'a, V: 'a + fmt::Debug> fmt::Debug for Values<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over the values of a `BTreeMap`. -/// -/// This `struct` is created by the [`values_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`values_mut`]: struct.BTreeMap.html#method.values_mut -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "map_values_mut", since = "1.10.0")] -#[derive(Debug)] -pub struct ValuesMut<'a, K: 'a, V: 'a> { - inner: IterMut<'a, K, V>, -} - -/// An iterator over a sub-range of entries in a `BTreeMap`. -/// -/// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`range`]: struct.BTreeMap.html#method.range -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct Range<'a, K: 'a, V: 'a> { - front: Handle, K, V, marker::Leaf>, marker::Edge>, - back: Handle, K, V, marker::Leaf>, marker::Edge>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Range<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self.clone()).finish() - } -} - -/// A mutable iterator over a sub-range of entries in a `BTreeMap`. -/// -/// This `struct` is created by the [`range_mut`] method on [`BTreeMap`]. See its -/// documentation for more. -/// -/// [`range_mut`]: struct.BTreeMap.html#method.range_mut -/// [`BTreeMap`]: struct.BTreeMap.html -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct RangeMut<'a, K: 'a, V: 'a> { - front: Handle, K, V, marker::Leaf>, marker::Edge>, - back: Handle, K, V, marker::Leaf>, marker::Edge>, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for RangeMut<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - let range = Range { - front: self.front.reborrow(), - back: self.back.reborrow(), - }; - f.debug_list().entries(range).finish() - } -} - -/// A view into a single entry in a map, which may either be vacant or occupied. -/// -/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. -/// -/// [`BTreeMap`]: struct.BTreeMap.html -/// [`entry`]: struct.BTreeMap.html#method.entry -#[stable(feature = "rust1", since = "1.0.0")] -pub enum Entry<'a, K: 'a, V: 'a> { - /// A vacant entry. - #[stable(feature = "rust1", since = "1.0.0")] - Vacant(#[stable(feature = "rust1", since = "1.0.0")] - VacantEntry<'a, K, V>), - - /// An occupied entry. - #[stable(feature = "rust1", since = "1.0.0")] - Occupied(#[stable(feature = "rust1", since = "1.0.0")] - OccupiedEntry<'a, K, V>), -} - -#[stable(feature= "debug_btree_map", since = "1.12.0")] -impl<'a, K: 'a + Debug + Ord, V: 'a + Debug> Debug for Entry<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - match *self { - Vacant(ref v) => f.debug_tuple("Entry") - .field(v) - .finish(), - Occupied(ref o) => f.debug_tuple("Entry") - .field(o) - .finish(), - } - } -} - -/// A view into a vacant entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VacantEntry<'a, K: 'a, V: 'a> { - key: K, - handle: Handle, K, V, marker::Leaf>, marker::Edge>, - length: &'a mut usize, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature= "debug_btree_map", since = "1.12.0")] -impl<'a, K: 'a + Debug + Ord, V: 'a> Debug for VacantEntry<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("VacantEntry") - .field(self.key()) - .finish() - } -} - -/// A view into an occupied entry in a `BTreeMap`. -/// It is part of the [`Entry`] enum. -/// -/// [`Entry`]: enum.Entry.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct OccupiedEntry<'a, K: 'a, V: 'a> { - handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, - - length: &'a mut usize, - - // Be invariant in `K` and `V` - _marker: PhantomData<&'a mut (K, V)>, -} - -#[stable(feature= "debug_btree_map", since = "1.12.0")] -impl<'a, K: 'a + Debug + Ord, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("OccupiedEntry") - .field("key", self.key()) - .field("value", self.get()) - .finish() - } -} - -// An iterator for merging two sorted sequences into one -struct MergeIter> { - left: Peekable, - right: Peekable, -} - -impl BTreeMap { - /// Makes a new empty BTreeMap with a reasonable choice for B. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// - /// // entries can now be inserted into the empty map - /// map.insert(1, "a"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BTreeMap { - BTreeMap { - root: node::Root::shared_empty_root(), - length: 0, - } - } - - /// Clears the map, removing all values. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.clear(); - /// assert!(a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - *self = BTreeMap::new(); - } - - /// Returns a reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get(&1), Some(&"a")); - /// assert_eq!(map.get(&2), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self, key: &Q) -> Option<&V> - where K: Borrow, - Q: Ord - { - match search::search_tree(self.root.as_ref(), key) { - Found(handle) => Some(handle.into_kv().1), - GoDown(_) => None, - } - } - - /// Returns the key-value pair corresponding to the supplied key. - /// - /// The supplied key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// ``` - /// #![feature(map_get_key_value)] - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); - /// assert_eq!(map.get_key_value(&2), None); - /// ``` - #[unstable(feature = "map_get_key_value", issue = "49347")] - pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> - where K: Borrow, - Q: Ord - { - match search::search_tree(self.root.as_ref(), k) { - Found(handle) => Some(handle.into_kv()), - GoDown(_) => None, - } - } - - /// Returns `true` if the map contains a value for the specified key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.contains_key(&1), true); - /// assert_eq!(map.contains_key(&2), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains_key(&self, key: &Q) -> bool - where K: Borrow, - Q: Ord - { - self.get(key).is_some() - } - - /// Returns a mutable reference to the value corresponding to the key. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// if let Some(x) = map.get_mut(&1) { - /// *x = "b"; - /// } - /// assert_eq!(map[&1], "b"); - /// ``` - // See `get` for implementation notes, this is basically a copy-paste with mut's added - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> - where K: Borrow, - Q: Ord - { - match search::search_tree(self.root.as_mut(), key) { - Found(handle) => Some(handle.into_kv_mut().1), - GoDown(_) => None, - } - } - - /// Inserts a key-value pair into the map. - /// - /// If the map did not have this key present, `None` is returned. - /// - /// If the map did have this key present, the value is updated, and the old - /// value is returned. The key is not updated, though; this matters for - /// types that can be `==` without being identical. See the [module-level - /// documentation] for more. - /// - /// [module-level documentation]: index.html#insert-and-complex-keys - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// assert_eq!(map.insert(37, "a"), None); - /// assert_eq!(map.is_empty(), false); - /// - /// map.insert(37, "b"); - /// assert_eq!(map.insert(37, "c"), Some("b")); - /// assert_eq!(map[&37], "c"); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, key: K, value: V) -> Option { - match self.entry(key) { - Occupied(mut entry) => Some(entry.insert(value)), - Vacant(entry) => { - entry.insert(value); - None - } - } - } - - /// Removes a key from the map, returning the value at the key if the key - /// was previously in the map. - /// - /// The key may be any borrowed form of the map's key type, but the ordering - /// on the borrowed form *must* match the ordering on the key type. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(1, "a"); - /// assert_eq!(map.remove(&1), Some("a")); - /// assert_eq!(map.remove(&1), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, key: &Q) -> Option - where K: Borrow, - Q: Ord - { - match search::search_tree(self.root.as_mut(), key) { - Found(handle) => { - Some(OccupiedEntry { - handle, - length: &mut self.length, - _marker: PhantomData, - } - .remove()) - } - GoDown(_) => None, - } - } - - /// Moves all elements from `other` into `Self`, leaving `other` empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// a.insert(3, "c"); - /// - /// let mut b = BTreeMap::new(); - /// b.insert(3, "d"); - /// b.insert(4, "e"); - /// b.insert(5, "f"); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.len(), 5); - /// assert_eq!(b.len(), 0); - /// - /// assert_eq!(a[&1], "a"); - /// assert_eq!(a[&2], "b"); - /// assert_eq!(a[&3], "d"); - /// assert_eq!(a[&4], "e"); - /// assert_eq!(a[&5], "f"); - /// ``` - #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - // Do we have to append anything at all? - if other.len() == 0 { - return; - } - - // We can just swap `self` and `other` if `self` is empty. - if self.len() == 0 { - mem::swap(self, other); - return; - } - - // First, we merge `self` and `other` into a sorted sequence in linear time. - let self_iter = mem::replace(self, BTreeMap::new()).into_iter(); - let other_iter = mem::replace(other, BTreeMap::new()).into_iter(); - let iter = MergeIter { - left: self_iter.peekable(), - right: other_iter.peekable(), - }; - - // Second, we build a tree from the sorted sequence in linear time. - self.from_sorted_iter(iter); - self.fix_right_edge(); - } - - /// Constructs a double-ended iterator over a sub-range of elements in the map. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Panics - /// - /// Panics if range `start > end`. - /// Panics if range `start == end` and both bounds are `Excluded`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::ops::Bound::Included; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(3, "a"); - /// map.insert(5, "b"); - /// map.insert(8, "c"); - /// for (&key, &value) in map.range((Included(&4), Included(&8))) { - /// println!("{}: {}", key, value); - /// } - /// assert_eq!(Some((&5, &"b")), map.range(4..).next()); - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range(&self, range: R) -> Range - where T: Ord, K: Borrow, R: RangeBounds - { - let root1 = self.root.as_ref(); - let root2 = self.root.as_ref(); - let (f, b) = range_search(root1, root2, range); - - Range { front: f, back: b} - } - - /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Panics - /// - /// Panics if range `start > end`. - /// Panics if range `start == end` and both bounds are `Excluded`. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter() - /// .map(|&s| (s, 0)) - /// .collect(); - /// for (_, balance) in map.range_mut("B".."Cheryl") { - /// *balance += 100; - /// } - /// for (name, balance) in &map { - /// println!("{} => {}", name, balance); - /// } - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range_mut(&mut self, range: R) -> RangeMut - where T: Ord, K: Borrow, R: RangeBounds - { - let root1 = self.root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - let (f, b) = range_search(root1, root2, range); - - RangeMut { - front: f, - back: b, - _marker: PhantomData, - } - } - - /// Gets the given key's corresponding entry in the map for in-place manipulation. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// // count the number of occurrences of letters in the vec - /// for x in vec!["a","b","a","c","a","b"] { - /// *count.entry(x).or_insert(0) += 1; - /// } - /// - /// assert_eq!(count["a"], 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn entry(&mut self, key: K) -> Entry { - // FIXME(@porglezomp) Avoid allocating if we don't insert - self.ensure_root_is_owned(); - match search::search_tree(self.root.as_mut(), &key) { - Found(handle) => { - Occupied(OccupiedEntry { - handle, - length: &mut self.length, - _marker: PhantomData, - }) - } - GoDown(handle) => { - Vacant(VacantEntry { - key, - handle, - length: &mut self.length, - _marker: PhantomData, - }) - } - } - } - - fn from_sorted_iter>(&mut self, iter: I) { - self.ensure_root_is_owned(); - let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node(); - // Iterate through all key-value pairs, pushing them into nodes at the right level. - for (key, value) in iter { - // Try to push key-value pair into the current leaf node. - if cur_node.len() < node::CAPACITY { - cur_node.push(key, value); - } else { - // No space left, go up and push there. - let mut open_node; - let mut test_node = cur_node.forget_type(); - loop { - match test_node.ascend() { - Ok(parent) => { - let parent = parent.into_node(); - if parent.len() < node::CAPACITY { - // Found a node with space left, push here. - open_node = parent; - break; - } else { - // Go up again. - test_node = parent.forget_type(); - } - } - Err(node) => { - // We are at the top, create a new root node and push there. - open_node = node.into_root_mut().push_level(); - break; - } - } - } - - // Push key-value pair and new right subtree. - let tree_height = open_node.height() - 1; - let mut right_tree = node::Root::new_leaf(); - for _ in 0..tree_height { - right_tree.push_level(); - } - open_node.push(key, value, right_tree); - - // Go down to the right-most leaf again. - cur_node = last_leaf_edge(open_node.forget_type()).into_node(); - } - - self.length += 1; - } - } - - fn fix_right_edge(&mut self) { - // Handle underfull nodes, start from the top. - let mut cur_node = self.root.as_mut(); - while let Internal(internal) = cur_node.force() { - // Check if right-most child is underfull. - let mut last_edge = internal.last_edge(); - let right_child_len = last_edge.reborrow().descend().len(); - if right_child_len < node::MIN_LEN { - // We need to steal. - let mut last_kv = match last_edge.left_kv() { - Ok(left) => left, - Err(_) => unreachable!(), - }; - last_kv.bulk_steal_left(node::MIN_LEN - right_child_len); - last_edge = last_kv.right_edge(); - } - - // Go further down. - cur_node = last_edge.descend(); - } - } - - /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "a"); - /// a.insert(2, "b"); - /// a.insert(3, "c"); - /// a.insert(17, "d"); - /// a.insert(41, "e"); - /// - /// let b = a.split_off(&3); - /// - /// assert_eq!(a.len(), 2); - /// assert_eq!(b.len(), 3); - /// - /// assert_eq!(a[&1], "a"); - /// assert_eq!(a[&2], "b"); - /// - /// assert_eq!(b[&3], "c"); - /// assert_eq!(b[&17], "d"); - /// assert_eq!(b[&41], "e"); - /// ``` - #[stable(feature = "btree_split_off", since = "1.11.0")] - pub fn split_off(&mut self, key: &Q) -> Self - where K: Borrow - { - if self.is_empty() { - return Self::new(); - } - - let total_num = self.len(); - - let mut right = Self::new(); - right.root = node::Root::new_leaf(); - for _ in 0..(self.root.as_ref().height()) { - right.root.push_level(); - } - - { - let mut left_node = self.root.as_mut(); - let mut right_node = right.root.as_mut(); - - loop { - let mut split_edge = match search::search_node(left_node, key) { - // key is going to the right tree - Found(handle) => handle.left_edge(), - GoDown(handle) => handle, - }; - - split_edge.move_suffix(&mut right_node); - - match (split_edge.force(), right_node.force()) { - (Internal(edge), Internal(node)) => { - left_node = edge.descend(); - right_node = node.first_edge().descend(); - } - (Leaf(_), Leaf(_)) => { - break; - } - _ => { - unreachable!(); - } - } - } - } - - self.fix_right_border(); - right.fix_left_border(); - - if self.root.as_ref().height() < right.root.as_ref().height() { - self.recalc_length(); - right.length = total_num - self.len(); - } else { - right.recalc_length(); - self.length = total_num - right.len(); - } - - right - } - - /// Calculates the number of elements if it is incorrect. - fn recalc_length(&mut self) { - fn dfs(node: NodeRef) -> usize { - let mut res = node.len(); - - if let Internal(node) = node.force() { - let mut edge = node.first_edge(); - loop { - res += dfs(edge.reborrow().descend()); - match edge.right_kv() { - Ok(right_kv) => { - edge = right_kv.right_edge(); - } - Err(_) => { - break; - } - } - } - } - - res - } - - self.length = dfs(self.root.as_ref()); - } - - /// Removes empty levels on the top. - fn fix_top(&mut self) { - loop { - { - let node = self.root.as_ref(); - if node.height() == 0 || node.len() > 0 { - break; - } - } - self.root.pop_level(); - } - } - - fn fix_right_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.root.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut last_kv = node.last_kv(); - - if last_kv.can_merge() { - cur_node = last_kv.merge().descend(); - } else { - let right_len = last_kv.reborrow().right_edge().descend().len(); - // `MINLEN + 1` to avoid readjust if merge happens on the next level. - if right_len < node::MIN_LEN + 1 { - last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); - } - cur_node = last_kv.right_edge().descend(); - } - } - } - - self.fix_top(); - } - - /// The symmetric clone of `fix_right_border`. - fn fix_left_border(&mut self) { - self.fix_top(); - - { - let mut cur_node = self.root.as_mut(); - - while let Internal(node) = cur_node.force() { - let mut first_kv = node.first_kv(); - - if first_kv.can_merge() { - cur_node = first_kv.merge().descend(); - } else { - let left_len = first_kv.reborrow().left_edge().descend().len(); - if left_len < node::MIN_LEN + 1 { - first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); - } - cur_node = first_kv.left_edge().descend(); - } - } - } - - self.fix_top(); - } - - /// If the root node is the shared root node, allocate our own node. - fn ensure_root_is_owned(&mut self) { - if self.root.is_shared_root() { - self.root = node::Root::new_leaf(); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap { - type Item = (&'a K, &'a V); - type IntoIter = Iter<'a, K, V>; - - fn into_iter(self) -> Iter<'a, K, V> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { - type Item = (&'a K, &'a V); - - fn next(&mut self) -> Option<(&'a K, &'a V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_unchecked()) } - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_back_unchecked()) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Clone for Iter<'a, K, V> { - fn clone(&self) -> Iter<'a, K, V> { - Iter { - range: self.range.clone(), - length: self.length, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap { - type Item = (&'a K, &'a mut V); - type IntoIter = IterMut<'a, K, V>; - - fn into_iter(self) -> IterMut<'a, K, V> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_unchecked()) } - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.length == 0 { - None - } else { - self.length -= 1; - unsafe { Some(self.range.next_back_unchecked()) } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BTreeMap { - type Item = (K, V); - type IntoIter = IntoIter; - - fn into_iter(self) -> IntoIter { - let root1 = unsafe { ptr::read(&self.root).into_ref() }; - let root2 = unsafe { ptr::read(&self.root).into_ref() }; - let len = self.length; - mem::forget(self); - - IntoIter { - front: first_leaf_edge(root1), - back: last_leaf_edge(root2), - length: len, - } - } -} - -#[stable(feature = "btree_drop", since = "1.7.0")] -impl Drop for IntoIter { - fn drop(&mut self) { - self.for_each(drop); - unsafe { - let leaf_node = ptr::read(&self.front).into_node(); - if leaf_node.is_shared_root() { - return; - } - - if let Some(first_parent) = leaf_node.deallocate_and_ascend() { - let mut cur_node = first_parent.into_node(); - while let Some(parent) = cur_node.deallocate_and_ascend() { - cur_node = parent.into_node() - } - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - if self.length == 0 { - return None; - } else { - self.length -= 1; - } - - let handle = unsafe { ptr::read(&self.front) }; - - let mut cur_handle = match handle.right_kv() { - Ok(kv) => { - let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; - let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; - self.front = kv.right_edge(); - return Some((k, v)); - } - Err(last_edge) => unsafe { - unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()) - }, - }; - - loop { - match cur_handle.right_kv() { - Ok(kv) => { - let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; - let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; - self.front = first_leaf_edge(kv.right_edge().descend()); - return Some((k, v)); - } - Err(last_edge) => unsafe { - cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()); - }, - } - } - } - - fn size_hint(&self) -> (usize, Option) { - (self.length, Some(self.length)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option<(K, V)> { - if self.length == 0 { - return None; - } else { - self.length -= 1; - } - - let handle = unsafe { ptr::read(&self.back) }; - - let mut cur_handle = match handle.left_kv() { - Ok(kv) => { - let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; - let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; - self.back = kv.left_edge(); - return Some((k, v)); - } - Err(last_edge) => unsafe { - unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()) - }, - }; - - loop { - match cur_handle.left_kv() { - Ok(kv) => { - let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; - let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; - self.back = last_leaf_edge(kv.left_edge().descend()); - return Some((k, v)); - } - Err(last_edge) => unsafe { - cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()); - }, - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { - self.length - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Keys<'a, K, V> { - type Item = &'a K; - - fn next(&mut self) -> Option<&'a K> { - self.inner.next().map(|(k, _)| k) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { - fn next_back(&mut self) -> Option<&'a K> { - self.inner.next_back().map(|(k, _)| k) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Clone for Keys<'a, K, V> { - fn clone(&self) -> Keys<'a, K, V> { - Keys { inner: self.inner.clone() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Iterator for Values<'a, K, V> { - type Item = &'a V; - - fn next(&mut self) -> Option<&'a V> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { - fn next_back(&mut self) -> Option<&'a V> { - self.inner.next_back().map(|(_, v)| v) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for Values<'a, K, V> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K, V> Clone for Values<'a, K, V> { - fn clone(&self) -> Values<'a, K, V> { - Values { inner: self.inner.clone() } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> Iterator for Range<'a, K, V> { - type Item = (&'a K, &'a V); - - fn next(&mut self) -> Option<(&'a K, &'a V)> { - if self.front == self.back { - None - } else { - unsafe { Some(self.next_unchecked()) } - } - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { - type Item = &'a mut V; - - fn next(&mut self) -> Option<&'a mut V> { - self.inner.next().map(|(_, v)| v) - } - - fn size_hint(&self) -> (usize, Option) { - self.inner.size_hint() - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { - fn next_back(&mut self) -> Option<&'a mut V> { - self.inner.next_back().map(|(_, v)| v) - } -} - -#[stable(feature = "map_values_mut", since = "1.10.0")] -impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { - fn len(&self) -> usize { - self.inner.len() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} - - -impl<'a, K, V> Range<'a, K, V> { - unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { - let handle = self.front; - - let mut cur_handle = match handle.right_kv() { - Ok(kv) => { - let ret = kv.into_kv(); - self.front = kv.right_edge(); - return ret; - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - unwrap_unchecked(next_level) - } - }; - - loop { - match cur_handle.right_kv() { - Ok(kv) => { - let ret = kv.into_kv(); - self.front = first_leaf_edge(kv.right_edge().descend()); - return ret; - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - cur_handle = unwrap_unchecked(next_level); - } - } - } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a V)> { - if self.front == self.back { - None - } else { - unsafe { Some(self.next_back_unchecked()) } - } - } -} - -impl<'a, K, V> Range<'a, K, V> { - unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { - let handle = self.back; - - let mut cur_handle = match handle.left_kv() { - Ok(kv) => { - let ret = kv.into_kv(); - self.back = kv.left_edge(); - return ret; - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - unwrap_unchecked(next_level) - } - }; - - loop { - match cur_handle.left_kv() { - Ok(kv) => { - let ret = kv.into_kv(); - self.back = last_leaf_edge(kv.left_edge().descend()); - return ret; - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - cur_handle = unwrap_unchecked(next_level); - } - } - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for Range<'a, K, V> {} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> Clone for Range<'a, K, V> { - fn clone(&self) -> Range<'a, K, V> { - Range { - front: self.front, - back: self.back, - } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> Iterator for RangeMut<'a, K, V> { - type Item = (&'a K, &'a mut V); - - fn next(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.front == self.back { - None - } else { - unsafe { Some(self.next_unchecked()) } - } - } -} - -impl<'a, K, V> RangeMut<'a, K, V> { - unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { - let handle = ptr::read(&self.front); - - let mut cur_handle = match handle.right_kv() { - Ok(kv) => { - let (k, v) = ptr::read(&kv).into_kv_mut(); - self.front = kv.right_edge(); - return (k, v); - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - unwrap_unchecked(next_level) - } - }; - - loop { - match cur_handle.right_kv() { - Ok(kv) => { - let (k, v) = ptr::read(&kv).into_kv_mut(); - self.front = first_leaf_edge(kv.right_edge().descend()); - return (k, v); - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - cur_handle = unwrap_unchecked(next_level); - } - } - } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { - fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { - if self.front == self.back { - None - } else { - unsafe { Some(self.next_back_unchecked()) } - } - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, K, V> FusedIterator for RangeMut<'a, K, V> {} - -impl<'a, K, V> RangeMut<'a, K, V> { - unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { - let handle = ptr::read(&self.back); - - let mut cur_handle = match handle.left_kv() { - Ok(kv) => { - let (k, v) = ptr::read(&kv).into_kv_mut(); - self.back = kv.left_edge(); - return (k, v); - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - unwrap_unchecked(next_level) - } - }; - - loop { - match cur_handle.left_kv() { - Ok(kv) => { - let (k, v) = ptr::read(&kv).into_kv_mut(); - self.back = last_leaf_edge(kv.left_edge().descend()); - return (k, v); - } - Err(last_edge) => { - let next_level = last_edge.into_node().ascend().ok(); - cur_handle = unwrap_unchecked(next_level); - } - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator<(K, V)> for BTreeMap { - fn from_iter>(iter: T) -> BTreeMap { - let mut map = BTreeMap::new(); - map.extend(iter); - map - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend<(K, V)> for BTreeMap { - #[inline] - fn extend>(&mut self, iter: T) { - for (k, v) in iter { - self.insert(k, v); - } - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for BTreeMap { - fn hash(&self, state: &mut H) { - for elt in self { - elt.hash(state); - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BTreeMap { - /// Creates an empty `BTreeMap`. - fn default() -> BTreeMap { - BTreeMap::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for BTreeMap { - fn eq(&self, other: &BTreeMap) -> bool { - self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for BTreeMap {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for BTreeMap { - #[inline] - fn partial_cmp(&self, other: &BTreeMap) -> Option { - self.iter().partial_cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for BTreeMap { - #[inline] - fn cmp(&self, other: &BTreeMap) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for BTreeMap { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_map().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap - where K: Borrow, - Q: Ord -{ - type Output = V; - - /// Returns a reference to the value corresponding to the supplied key. - /// - /// # Panics - /// - /// Panics if the key is not present in the `BTreeMap`. - #[inline] - fn index(&self, key: &Q) -> &V { - self.get(key).expect("no entry found for key") - } -} - -fn first_leaf_edge - (mut node: NodeRef) - -> Handle, marker::Edge> { - loop { - match node.force() { - Leaf(leaf) => return leaf.first_edge(), - Internal(internal) => { - node = internal.first_edge().descend(); - } - } - } -} - -fn last_leaf_edge - (mut node: NodeRef) - -> Handle, marker::Edge> { - loop { - match node.force() { - Leaf(leaf) => return leaf.last_edge(), - Internal(internal) => { - node = internal.last_edge().descend(); - } - } - } -} - -fn range_search>( - root1: NodeRef, - root2: NodeRef, - range: R -)-> (Handle, marker::Edge>, - Handle, marker::Edge>) - where Q: Ord, K: Borrow -{ - match (range.start_bound(), range.end_bound()) { - (Excluded(s), Excluded(e)) if s==e => - panic!("range start and end are equal and excluded in BTreeMap"), - (Included(s), Included(e)) | - (Included(s), Excluded(e)) | - (Excluded(s), Included(e)) | - (Excluded(s), Excluded(e)) if s>e => - panic!("range start is greater than range end in BTreeMap"), - _ => {}, - }; - - let mut min_node = root1; - let mut max_node = root2; - let mut min_found = false; - let mut max_found = false; - let mut diverged = false; - - loop { - let min_edge = match (min_found, range.start_bound()) { - (false, Included(key)) => match search::search_linear(&min_node, key) { - (i, true) => { min_found = true; i }, - (i, false) => i, - }, - (false, Excluded(key)) => match search::search_linear(&min_node, key) { - (i, true) => { min_found = true; i+1 }, - (i, false) => i, - }, - (_, Unbounded) => 0, - (true, Included(_)) => min_node.keys().len(), - (true, Excluded(_)) => 0, - }; - - let max_edge = match (max_found, range.end_bound()) { - (false, Included(key)) => match search::search_linear(&max_node, key) { - (i, true) => { max_found = true; i+1 }, - (i, false) => i, - }, - (false, Excluded(key)) => match search::search_linear(&max_node, key) { - (i, true) => { max_found = true; i }, - (i, false) => i, - }, - (_, Unbounded) => max_node.keys().len(), - (true, Included(_)) => 0, - (true, Excluded(_)) => max_node.keys().len(), - }; - - if !diverged { - if max_edge < min_edge { panic!("Ord is ill-defined in BTreeMap range") } - if min_edge != max_edge { diverged = true; } - } - - let front = Handle::new_edge(min_node, min_edge); - let back = Handle::new_edge(max_node, max_edge); - match (front.force(), back.force()) { - (Leaf(f), Leaf(b)) => { - return (f, b); - }, - (Internal(min_int), Internal(max_int)) => { - min_node = min_int.descend(); - max_node = max_int.descend(); - }, - _ => unreachable!("BTreeMap has different depths"), - }; - } -} - -#[inline(always)] -unsafe fn unwrap_unchecked(val: Option) -> T { - val.unwrap_or_else(|| { - if cfg!(debug_assertions) { - panic!("'unchecked' unwrap on None in BTreeMap"); - } else { - intrinsics::unreachable(); - } - }) -} - -impl BTreeMap { - /// Gets an iterator over the entries of the map, sorted by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert(3, "c"); - /// map.insert(2, "b"); - /// map.insert(1, "a"); - /// - /// for (key, value) in map.iter() { - /// println!("{}: {}", key, value); - /// } - /// - /// let (first_key, first_value) = map.iter().next().unwrap(); - /// assert_eq!((*first_key, *first_value), (1, "a")); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { - range: Range { - front: first_leaf_edge(self.root.as_ref()), - back: last_leaf_edge(self.root.as_ref()), - }, - length: self.length, - } - } - - /// Gets a mutable iterator over the entries of the map, sorted by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map = BTreeMap::new(); - /// map.insert("a", 1); - /// map.insert("b", 2); - /// map.insert("c", 3); - /// - /// // add 10 to the value if the key isn't "a" - /// for (key, value) in map.iter_mut() { - /// if key != &"a" { - /// *value += 10; - /// } - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut { - let root1 = self.root.as_mut(); - let root2 = unsafe { ptr::read(&root1) }; - IterMut { - range: RangeMut { - front: first_leaf_edge(root1), - back: last_leaf_edge(root2), - _marker: PhantomData, - }, - length: self.length, - } - } - - /// Gets an iterator over the keys of the map, in sorted order. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(2, "b"); - /// a.insert(1, "a"); - /// - /// let keys: Vec<_> = a.keys().cloned().collect(); - /// assert_eq!(keys, [1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn keys<'a>(&'a self) -> Keys<'a, K, V> { - Keys { inner: self.iter() } - } - - /// Gets an iterator over the values of the map, in order by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, "hello"); - /// a.insert(2, "goodbye"); - /// - /// let values: Vec<&str> = a.values().cloned().collect(); - /// assert_eq!(values, ["hello", "goodbye"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn values<'a>(&'a self) -> Values<'a, K, V> { - Values { inner: self.iter() } - } - - /// Gets a mutable iterator over the values of the map, in order by key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// a.insert(1, String::from("hello")); - /// a.insert(2, String::from("goodbye")); - /// - /// for value in a.values_mut() { - /// value.push_str("!"); - /// } - /// - /// let values: Vec = a.values().cloned().collect(); - /// assert_eq!(values, [String::from("hello!"), - /// String::from("goodbye!")]); - /// ``` - #[stable(feature = "map_values_mut", since = "1.10.0")] - pub fn values_mut(&mut self) -> ValuesMut { - ValuesMut { inner: self.iter_mut() } - } - - /// Returns the number of elements in the map. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// assert_eq!(a.len(), 0); - /// a.insert(1, "a"); - /// assert_eq!(a.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.length - } - - /// Returns `true` if the map contains no elements. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut a = BTreeMap::new(); - /// assert!(a.is_empty()); - /// a.insert(1, "a"); - /// assert!(!a.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -impl<'a, K: Ord, V> Entry<'a, K, V> { - /// Ensures a value is in the entry by inserting the default if empty, and returns - /// a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert(self, default: V) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default), - } - } - - /// Ensures a value is in the entry by inserting the result of the default function if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); - /// let s = "hoho".to_string(); - /// - /// map.entry("poneyland").or_insert_with(|| s); - /// - /// assert_eq!(map["poneyland"], "hoho".to_string()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn or_insert_with V>(self, default: F) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(default()), - } - } - - /// Returns a reference to this entry's key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - match *self { - Occupied(ref entry) => entry.key(), - Vacant(ref entry) => entry.key(), - } - } - - /// Provides in-place mutable access to an occupied entry before any - /// potential inserts into the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 42); - /// - /// map.entry("poneyland") - /// .and_modify(|e| { *e += 1 }) - /// .or_insert(42); - /// assert_eq!(map["poneyland"], 43); - /// ``` - #[stable(feature = "entry_and_modify", since = "1.26.0")] - pub fn and_modify(self, f: F) -> Self - where F: FnOnce(&mut V) - { - match self { - Occupied(mut entry) => { - f(entry.get_mut()); - Occupied(entry) - }, - Vacant(entry) => Vacant(entry), - } - } -} - -impl<'a, K: Ord, V: Default> Entry<'a, K, V> { - #[stable(feature = "entry_or_default", since = "1.28.0")] - /// Ensures a value is in the entry by inserting the default value if empty, - /// and returns a mutable reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// # fn main() { - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); - /// map.entry("poneyland").or_default(); - /// - /// assert_eq!(map["poneyland"], None); - /// # } - /// ``` - pub fn or_default(self) -> &'a mut V { - match self { - Occupied(entry) => entry.into_mut(), - Vacant(entry) => entry.insert(Default::default()), - } - } - -} - -impl<'a, K: Ord, V> VacantEntry<'a, K, V> { - /// Gets a reference to the key that would be used when inserting a value - /// through the VacantEntry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - &self.key - } - - /// Take ownership of the key. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// if let Entry::Vacant(v) = map.entry("poneyland") { - /// v.into_key(); - /// } - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn into_key(self) -> K { - self.key - } - - /// Sets the value of the entry with the `VacantEntry`'s key, - /// and returns a mutable reference to it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); - /// - /// // count the number of occurrences of letters in the vec - /// for x in vec!["a","b","a","c","a","b"] { - /// *count.entry(x).or_insert(0) += 1; - /// } - /// - /// assert_eq!(count["a"], 3); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(self, value: V) -> &'a mut V { - *self.length += 1; - - let out_ptr; - - let mut ins_k; - let mut ins_v; - let mut ins_edge; - - let mut cur_parent = match self.handle.insert(self.key, value) { - (Fit(handle), _) => return handle.into_kv_mut().1, - (Split(left, k, v, right), ptr) => { - ins_k = k; - ins_v = v; - ins_edge = right; - out_ptr = ptr; - left.ascend().map_err(|n| n.into_root_mut()) - } - }; - - loop { - match cur_parent { - Ok(parent) => { - match parent.insert(ins_k, ins_v, ins_edge) { - Fit(_) => return unsafe { &mut *out_ptr }, - Split(left, k, v, right) => { - ins_k = k; - ins_v = v; - ins_edge = right; - cur_parent = left.ascend().map_err(|n| n.into_root_mut()); - } - } - } - Err(root) => { - root.push_level().push(ins_k, ins_v, ins_edge); - return unsafe { &mut *out_ptr }; - } - } - } - } -} - -impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { - /// Gets a reference to the key in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); - /// ``` - #[stable(feature = "map_entry_keys", since = "1.10.0")] - pub fn key(&self) -> &K { - self.handle.reborrow().into_kv().0 - } - - /// Take ownership of the key and value from the map. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// // We delete the entry from the map. - /// o.remove_entry(); - /// } - /// - /// // If now try to get the value, it will panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] - pub fn remove_entry(self) -> (K, V) { - self.remove_kv() - } - - /// Gets a reference to the value in the entry. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.get(), &12); - /// } - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self) -> &V { - self.handle.reborrow().into_kv().1 - } - - /// Gets a mutable reference to the value in the entry. - /// - /// If you need a reference to the `OccupiedEntry` which may outlive the - /// destruction of the `Entry` value, see [`into_mut`]. - /// - /// [`into_mut`]: #method.into_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// *o.get_mut() += 10; - /// assert_eq!(*o.get(), 22); - /// - /// // We can use the same Entry multiple times. - /// *o.get_mut() += 2; - /// } - /// assert_eq!(map["poneyland"], 24); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self) -> &mut V { - self.handle.kv_mut().1 - } - - /// Converts the entry into a mutable reference to its value. - /// - /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. - /// - /// [`get_mut`]: #method.get_mut - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// assert_eq!(map["poneyland"], 12); - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// *o.into_mut() += 10; - /// } - /// assert_eq!(map["poneyland"], 22); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn into_mut(self) -> &'a mut V { - self.handle.into_kv_mut().1 - } - - /// Sets the value of the entry with the `OccupiedEntry`'s key, - /// and returns the entry's old value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(mut o) = map.entry("poneyland") { - /// assert_eq!(o.insert(15), 12); - /// } - /// assert_eq!(map["poneyland"], 15); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: V) -> V { - mem::replace(self.get_mut(), value) - } - - /// Takes the value of the entry out of the map, and returns it. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeMap; - /// use std::collections::btree_map::Entry; - /// - /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); - /// map.entry("poneyland").or_insert(12); - /// - /// if let Entry::Occupied(o) = map.entry("poneyland") { - /// assert_eq!(o.remove(), 12); - /// } - /// // If we try to get "poneyland"'s value, it'll panic: - /// // println!("{}", map["poneyland"]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(self) -> V { - self.remove_kv().1 - } - - fn remove_kv(self) -> (K, V) { - *self.length -= 1; - - let (small_leaf, old_key, old_val) = match self.handle.force() { - Leaf(leaf) => { - let (hole, old_key, old_val) = leaf.remove(); - (hole.into_node(), old_key, old_val) - } - Internal(mut internal) => { - let key_loc = internal.kv_mut().0 as *mut K; - let val_loc = internal.kv_mut().1 as *mut V; - - let to_remove = first_leaf_edge(internal.right_edge().descend()).right_kv().ok(); - let to_remove = unsafe { unwrap_unchecked(to_remove) }; - - let (hole, key, val) = to_remove.remove(); - - let old_key = unsafe { mem::replace(&mut *key_loc, key) }; - let old_val = unsafe { mem::replace(&mut *val_loc, val) }; - - (hole.into_node(), old_key, old_val) - } - }; - - // Handle underflow - let mut cur_node = small_leaf.forget_type(); - while cur_node.len() < node::CAPACITY / 2 { - match handle_underfull_node(cur_node) { - AtRoot => break, - EmptyParent(_) => unreachable!(), - Merged(parent) => { - if parent.len() == 0 { - // We must be at the root - parent.into_root_mut().pop_level(); - break; - } else { - cur_node = parent.forget_type(); - } - } - Stole(_) => break, - } - } - - (old_key, old_val) - } -} - -enum UnderflowResult<'a, K, V> { - AtRoot, - EmptyParent(NodeRef, K, V, marker::Internal>), - Merged(NodeRef, K, V, marker::Internal>), - Stole(NodeRef, K, V, marker::Internal>), -} - -fn handle_underfull_node<'a, K, V>(node: NodeRef, K, V, marker::LeafOrInternal>) - -> UnderflowResult<'a, K, V> { - let parent = if let Ok(parent) = node.ascend() { - parent - } else { - return AtRoot; - }; - - let (is_left, mut handle) = match parent.left_kv() { - Ok(left) => (true, left), - Err(parent) => { - match parent.right_kv() { - Ok(right) => (false, right), - Err(parent) => { - return EmptyParent(parent.into_node()); - } - } - } - }; - - if handle.can_merge() { - Merged(handle.merge().into_node()) - } else { - if is_left { - handle.steal_left(); - } else { - handle.steal_right(); - } - Stole(handle.into_node()) - } -} - -impl> Iterator for MergeIter { - type Item = (K, V); - - fn next(&mut self) -> Option<(K, V)> { - let res = match (self.left.peek(), self.right.peek()) { - (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), - (Some(_), None) => Ordering::Less, - (None, Some(_)) => Ordering::Greater, - (None, None) => return None, - }; - - // Check which elements comes first and only advance the corresponding iterator. - // If two keys are equal, take the value from `right`. - match res { - Ordering::Less => self.left.next(), - Ordering::Greater => self.right.next(), - Ordering::Equal => { - self.left.next(); - self.right.next() - } - } - } -} diff --git a/src/liballoc/btree/mod.rs b/src/liballoc/btree/mod.rs deleted file mode 100644 index 087c9f228d4..00000000000 --- a/src/liballoc/btree/mod.rs +++ /dev/null @@ -1,23 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -mod node; -mod search; -pub mod map; -pub mod set; - -#[doc(hidden)] -trait Recover { - type Key; - - fn get(&self, key: &Q) -> Option<&Self::Key>; - fn take(&mut self, key: &Q) -> Option; - fn replace(&mut self, key: Self::Key) -> Option; -} diff --git a/src/liballoc/btree/node.rs b/src/liballoc/btree/node.rs deleted file mode 100644 index 19bdcbc6ad6..00000000000 --- a/src/liballoc/btree/node.rs +++ /dev/null @@ -1,1622 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This is an attempt at an implementation following the ideal -// -// ``` -// struct BTreeMap { -// height: usize, -// root: Option>> -// } -// -// struct Node { -// keys: [K; 2 * B - 1], -// vals: [V; 2 * B - 1], -// edges: if height > 0 { -// [Box>; 2 * B] -// } else { () }, -// parent: *const Node, -// parent_idx: u16, -// len: u16, -// } -// ``` -// -// Since Rust doesn't actually have dependent types and polymorphic recursion, -// we make do with lots of unsafety. - -// A major goal of this module is to avoid complexity by treating the tree as a generic (if -// weirdly shaped) container and avoiding dealing with most of the B-Tree invariants. As such, -// this module doesn't care whether the entries are sorted, which nodes can be underfull, or -// even what underfull means. However, we do rely on a few invariants: -// -// - Trees must have uniform depth/height. This means that every path down to a leaf from a -// given node has exactly the same length. -// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. -// This implies that even an empty internal node has at least one edge. - -use core::marker::PhantomData; -use core::mem; -use core::ptr::{self, Unique, NonNull}; -use core::slice; - -use alloc::{Global, Alloc, Layout}; -use boxed::Box; - -const B: usize = 6; -pub const MIN_LEN: usize = B - 1; -pub const CAPACITY: usize = 2 * B - 1; - -/// The underlying representation of leaf nodes. Note that it is often unsafe to actually store -/// these, since only the first `len` keys and values are assumed to be initialized. As such, -/// these should always be put behind pointers, and specifically behind `BoxedNode` in the owned -/// case. -/// -/// See also rust-lang/rfcs#197, which would make this structure significantly more safe by -/// avoiding accidentally dropping unused and uninitialized keys and values. -/// -/// We put the metadata first so that its position is the same for every `K` and `V`, in order -/// to statically allocate a single dummy node to avoid allocations. This struct is `repr(C)` to -/// prevent them from being reordered. -#[repr(C)] -struct LeafNode { - /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. - /// This either points to an actual node or is null. - parent: *const InternalNode, - - /// This node's index into the parent node's `edges` array. - /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. - /// This is only guaranteed to be initialized when `parent` is nonnull. - parent_idx: u16, - - /// The number of keys and values this node stores. - /// - /// This next to `parent_idx` to encourage the compiler to join `len` and - /// `parent_idx` into the same 32-bit word, reducing space overhead. - len: u16, - - /// The arrays storing the actual data of the node. Only the first `len` elements of each - /// array are initialized and valid. - keys: [K; CAPACITY], - vals: [V; CAPACITY], -} - -impl LeafNode { - /// Creates a new `LeafNode`. Unsafe because all nodes should really be hidden behind - /// `BoxedNode`, preventing accidental dropping of uninitialized keys and values. - unsafe fn new() -> Self { - LeafNode { - // As a general policy, we leave fields uninitialized if they can be, as this should - // be both slightly faster and easier to track in Valgrind. - keys: mem::uninitialized(), - vals: mem::uninitialized(), - parent: ptr::null(), - parent_idx: mem::uninitialized(), - len: 0 - } - } - - fn is_shared_root(&self) -> bool { - self as *const _ == &EMPTY_ROOT_NODE as *const _ as *const LeafNode - } -} - -// We need to implement Sync here in order to make a static instance. -unsafe impl Sync for LeafNode<(), ()> {} - -// An empty node used as a placeholder for the root node, to avoid allocations. -// We use () in order to save space, since no operation on an empty tree will -// ever take a pointer past the first key. -static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode { - parent: ptr::null(), - parent_idx: 0, - len: 0, - keys: [(); CAPACITY], - vals: [(); CAPACITY], -}; - -/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden -/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an -/// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the -/// node, allowing code to act on leaf and internal nodes generically without having to even check -/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`. -#[repr(C)] -struct InternalNode { - data: LeafNode, - - /// The pointers to the children of this node. `len + 1` of these are considered - /// initialized and valid. - edges: [BoxedNode; 2 * B], -} - -impl InternalNode { - /// Creates a new `InternalNode`. - /// - /// This is unsafe for two reasons. First, it returns an `InternalNode` by value, risking - /// dropping of uninitialized fields. Second, an invariant of internal nodes is that `len + 1` - /// edges are initialized and valid, meaning that even when the node is empty (having a - /// `len` of 0), there must be one initialized and valid edge. This function does not set up - /// such an edge. - unsafe fn new() -> Self { - InternalNode { - data: LeafNode::new(), - edges: mem::uninitialized() - } - } -} - -/// An owned pointer to a node. This basically is either `Box>` or -/// `Box>`. However, it contains no information as to which of the two types -/// of nodes is actually behind the box, and, partially due to this lack of information, has no -/// destructor. -struct BoxedNode { - ptr: Unique> -} - -impl BoxedNode { - fn from_leaf(node: Box>) -> Self { - BoxedNode { ptr: Box::into_unique(node) } - } - - fn from_internal(node: Box>) -> Self { - unsafe { - BoxedNode { ptr: Unique::new_unchecked(Box::into_raw(node) as *mut LeafNode) } - } - } - - unsafe fn from_ptr(ptr: NonNull>) -> Self { - BoxedNode { ptr: Unique::from(ptr) } - } - - fn as_ptr(&self) -> NonNull> { - NonNull::from(self.ptr) - } -} - -/// An owned tree. Note that despite being owned, this does not have a destructor, -/// and must be cleaned up manually. -pub struct Root { - node: BoxedNode, - height: usize -} - -unsafe impl Sync for Root { } -unsafe impl Send for Root { } - -impl Root { - pub fn is_shared_root(&self) -> bool { - self.as_ref().is_shared_root() - } - - pub fn shared_empty_root() -> Self { - Root { - node: unsafe { - BoxedNode::from_ptr(NonNull::new_unchecked( - &EMPTY_ROOT_NODE as *const _ as *const LeafNode as *mut _ - )) - }, - height: 0, - } - } - - pub fn new_leaf() -> Self { - Root { - node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), - height: 0 - } - } - - pub fn as_ref(&self) - -> NodeRef { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: self as *const _ as *mut _, - _marker: PhantomData, - } - } - - pub fn as_mut(&mut self) - -> NodeRef { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: self as *mut _, - _marker: PhantomData, - } - } - - pub fn into_ref(self) - -> NodeRef { - NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: ptr::null_mut(), // FIXME: Is there anything better to do here? - _marker: PhantomData, - } - } - - /// Adds a new internal node with a single edge, pointing to the previous root, and make that - /// new node the root. This increases the height by 1 and is the opposite of `pop_level`. - pub fn push_level(&mut self) - -> NodeRef { - debug_assert!(!self.is_shared_root()); - let mut new_node = Box::new(unsafe { InternalNode::new() }); - new_node.edges[0] = unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }; - - self.node = BoxedNode::from_internal(new_node); - self.height += 1; - - let mut ret = NodeRef { - height: self.height, - node: self.node.as_ptr(), - root: self as *mut _, - _marker: PhantomData - }; - - unsafe { - ret.reborrow_mut().first_edge().correct_parent_link(); - } - - ret - } - - /// Removes the root node, using its first child as the new root. This cannot be called when - /// the tree consists only of a leaf node. As it is intended only to be called when the root - /// has only one edge, no cleanup is done on any of the other children are elements of the root. - /// This decreases the height by 1 and is the opposite of `push_level`. - pub fn pop_level(&mut self) { - debug_assert!(self.height > 0); - - let top = self.node.ptr; - - self.node = unsafe { - BoxedNode::from_ptr(self.as_mut() - .cast_unchecked::() - .first_edge() - .descend() - .node) - }; - self.height -= 1; - self.as_mut().as_leaf_mut().parent = ptr::null(); - - unsafe { - Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); - } - } -} - -// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` -// is `Mut`. This is technically wrong, but cannot result in any unsafety due to -// internal use of `NodeRef` because we stay completely generic over `K` and `V`. -// However, whenever a public type wraps `NodeRef`, make sure that it has the -// correct variance. -/// A reference to a node. -/// -/// This type has a number of parameters that controls how it acts: -/// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`. -/// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, -/// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, -/// and when this is `Owned`, the `NodeRef` acts roughly like `Box`. -/// - `K` and `V`: These control what types of things are stored in the nodes. -/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is -/// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the -/// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the -/// `NodeRef` could be pointing to either type of node. -pub struct NodeRef { - height: usize, - node: NonNull>, - // This is null unless the borrow type is `Mut` - root: *const Root, - _marker: PhantomData<(BorrowType, Type)> -} - -impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> { } -impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { - fn clone(&self) -> Self { - *self - } -} - -unsafe impl Sync - for NodeRef { } - -unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send - for NodeRef, K, V, Type> { } -unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send - for NodeRef, K, V, Type> { } -unsafe impl Send - for NodeRef { } - -impl NodeRef { - fn as_internal(&self) -> &InternalNode { - unsafe { - &*(self.node.as_ptr() as *mut InternalNode) - } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - fn as_internal_mut(&mut self) -> &mut InternalNode { - unsafe { - &mut *(self.node.as_ptr() as *mut InternalNode) - } - } -} - - -impl NodeRef { - /// Finds the length of the node. This is the number of keys or values. In an - /// internal node, the number of edges is `len() + 1`. - pub fn len(&self) -> usize { - self.as_leaf().len as usize - } - - /// Returns the height of this node in the whole tree. Zero height denotes the - /// leaf level. - pub fn height(&self) -> usize { - self.height - } - - /// Removes any static information about whether this node is a `Leaf` or an - /// `Internal` node. - pub fn forget_type(self) -> NodeRef { - NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - } - } - - /// Temporarily takes out another, immutable reference to the same node. - fn reborrow<'a>(&'a self) -> NodeRef, K, V, Type> { - NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - } - } - - fn as_leaf(&self) -> &LeafNode { - unsafe { - self.node.as_ref() - } - } - - pub fn is_shared_root(&self) -> bool { - self.as_leaf().is_shared_root() - } - - pub fn keys(&self) -> &[K] { - self.reborrow().into_key_slice() - } - - fn vals(&self) -> &[V] { - self.reborrow().into_val_slice() - } - - /// Finds the parent of the current node. Returns `Ok(handle)` if the current - /// node actually has a parent, where `handle` points to the edge of the parent - /// that points to the current node. Returns `Err(self)` if the current node has - /// no parent, giving back the original `NodeRef`. - /// - /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should - /// both, upon success, do nothing. - pub fn ascend(self) -> Result< - Handle< - NodeRef< - BorrowType, - K, V, - marker::Internal - >, - marker::Edge - >, - Self - > { - let parent_as_leaf = self.as_leaf().parent as *const LeafNode; - if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { - Ok(Handle { - node: NodeRef { - height: self.height + 1, - node: non_zero, - root: self.root, - _marker: PhantomData - }, - idx: self.as_leaf().parent_idx as usize, - _marker: PhantomData - }) - } else { - Err(self) - } - } - - pub fn first_edge(self) -> Handle { - Handle::new_edge(self, 0) - } - - pub fn last_edge(self) -> Handle { - let len = self.len(); - Handle::new_edge(self, len) - } - - /// Note that `self` must be nonempty. - pub fn first_kv(self) -> Handle { - debug_assert!(self.len() > 0); - Handle::new_kv(self, 0) - } - - /// Note that `self` must be nonempty. - pub fn last_kv(self) -> Handle { - let len = self.len(); - debug_assert!(len > 0); - Handle::new_kv(self, len - 1) - } -} - -impl NodeRef { - /// Similar to `ascend`, gets a reference to a node's parent node, but also - /// deallocate the current node in the process. This is unsafe because the - /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend(self) -> Option< - Handle< - NodeRef< - marker::Owned, - K, V, - marker::Internal - >, - marker::Edge - > - > { - debug_assert!(!self.is_shared_root()); - let node = self.node; - let ret = self.ascend().ok(); - Global.dealloc(node.cast(), Layout::new::>()); - ret - } -} - -impl NodeRef { - /// Similar to `ascend`, gets a reference to a node's parent node, but also - /// deallocate the current node in the process. This is unsafe because the - /// current node will still be accessible despite being deallocated. - pub unsafe fn deallocate_and_ascend(self) -> Option< - Handle< - NodeRef< - marker::Owned, - K, V, - marker::Internal - >, - marker::Edge - > - > { - let node = self.node; - let ret = self.ascend().ok(); - Global.dealloc(node.cast(), Layout::new::>()); - ret - } -} - -impl<'a, K, V, Type> NodeRef, K, V, Type> { - /// Unsafely asserts to the compiler some static information about whether this - /// node is a `Leaf`. - unsafe fn cast_unchecked(&mut self) - -> NodeRef { - - NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - } - } - - /// Temporarily takes out another, mutable reference to the same node. Beware, as - /// this method is very dangerous, doubly so since it may not immediately appear - /// dangerous. - /// - /// Because mutable pointers can roam anywhere around the tree and can even (through - /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` - /// can easily be used to make the original mutable pointer dangling, or, in the case - /// of a reborrowed handle, out of bounds. - // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts - // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. - unsafe fn reborrow_mut(&mut self) -> NodeRef { - NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - } - } - - fn as_leaf_mut(&mut self) -> &mut LeafNode { - unsafe { - self.node.as_mut() - } - } - - fn keys_mut(&mut self) -> &mut [K] { - unsafe { self.reborrow_mut().into_key_slice_mut() } - } - - fn vals_mut(&mut self) -> &mut [V] { - unsafe { self.reborrow_mut().into_val_slice_mut() } - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - fn into_key_slice(self) -> &'a [K] { - // When taking a pointer to the keys, if our key has a stricter - // alignment requirement than the shared root does, then the pointer - // would be out of bounds, which LLVM assumes will not happen. If the - // alignment is more strict, we need to make an empty slice that doesn't - // use an out of bounds pointer. - if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { - &[] - } else { - // Here either it's not the root, or the alignment is less strict, - // in which case the keys pointer will point "one-past-the-end" of - // the node, which is allowed by LLVM. - unsafe { - slice::from_raw_parts( - self.as_leaf().keys.as_ptr(), - self.len() - ) - } - } - } - - fn into_val_slice(self) -> &'a [V] { - debug_assert!(!self.is_shared_root()); - unsafe { - slice::from_raw_parts( - self.as_leaf().vals.as_ptr(), - self.len() - ) - } - } - - fn into_slices(self) -> (&'a [K], &'a [V]) { - let k = unsafe { ptr::read(&self) }; - (k.into_key_slice(), self.into_val_slice()) - } -} - -impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { - /// Gets a mutable reference to the root itself. This is useful primarily when the - /// height of the tree needs to be adjusted. Never call this on a reborrowed pointer. - pub fn into_root_mut(self) -> &'a mut Root { - unsafe { - &mut *(self.root as *mut Root) - } - } - - fn into_key_slice_mut(mut self) -> &'a mut [K] { - if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { - &mut [] - } else { - unsafe { - slice::from_raw_parts_mut( - &mut self.as_leaf_mut().keys as *mut [K] as *mut K, - self.len() - ) - } - } - } - - fn into_val_slice_mut(mut self) -> &'a mut [V] { - debug_assert!(!self.is_shared_root()); - unsafe { - slice::from_raw_parts_mut( - &mut self.as_leaf_mut().vals as *mut [V] as *mut V, - self.len() - ) - } - } - - fn into_slices_mut(self) -> (&'a mut [K], &'a mut [V]) { - let k = unsafe { ptr::read(&self) }; - (k.into_key_slice_mut(), self.into_val_slice_mut()) - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Leaf> { - /// Adds a key/value pair the end of the node. - pub fn push(&mut self, key: K, val: V) { - // Necessary for correctness, but this is an internal module - debug_assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); - - let idx = self.len(); - - unsafe { - ptr::write(self.keys_mut().get_unchecked_mut(idx), key); - ptr::write(self.vals_mut().get_unchecked_mut(idx), val); - } - - self.as_leaf_mut().len += 1; - } - - /// Adds a key/value pair to the beginning of the node. - pub fn push_front(&mut self, key: K, val: V) { - // Necessary for correctness, but this is an internal module - debug_assert!(self.len() < CAPACITY); - debug_assert!(!self.is_shared_root()); - - unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); - } - - self.as_leaf_mut().len += 1; - } -} - -impl<'a, K, V> NodeRef, K, V, marker::Internal> { - /// Adds a key/value pair and an edge to go to the right of that pair to - /// the end of the node. - pub fn push(&mut self, key: K, val: V, edge: Root) { - // Necessary for correctness, but this is an internal module - debug_assert!(edge.height == self.height - 1); - debug_assert!(self.len() < CAPACITY); - - let idx = self.len(); - - unsafe { - ptr::write(self.keys_mut().get_unchecked_mut(idx), key); - ptr::write(self.vals_mut().get_unchecked_mut(idx), val); - ptr::write(self.as_internal_mut().edges.get_unchecked_mut(idx + 1), edge.node); - - self.as_leaf_mut().len += 1; - - Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); - } - } - - fn correct_childrens_parent_links(&mut self, first: usize, after_last: usize) { - for i in first..after_last { - Handle::new_edge(unsafe { self.reborrow_mut() }, i).correct_parent_link(); - } - } - - fn correct_all_childrens_parent_links(&mut self) { - let len = self.len(); - self.correct_childrens_parent_links(0, len + 1); - } - - /// Adds a key/value pair and an edge to go to the left of that pair to - /// the beginning of the node. - pub fn push_front(&mut self, key: K, val: V, edge: Root) { - // Necessary for correctness, but this is an internal module - debug_assert!(edge.height == self.height - 1); - debug_assert!(self.len() < CAPACITY); - - unsafe { - slice_insert(self.keys_mut(), 0, key); - slice_insert(self.vals_mut(), 0, val); - slice_insert( - slice::from_raw_parts_mut( - self.as_internal_mut().edges.as_mut_ptr(), - self.len()+1 - ), - 0, - edge.node - ); - - self.as_leaf_mut().len += 1; - - self.correct_all_childrens_parent_links(); - } - } -} - -impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { - /// Removes a key/value pair from the end of this node. If this is an internal node, - /// also removes the edge that was to the right of that pair. - pub fn pop(&mut self) -> (K, V, Option>) { - // Necessary for correctness, but this is an internal module - debug_assert!(self.len() > 0); - - let idx = self.len() - 1; - - unsafe { - let key = ptr::read(self.keys().get_unchecked(idx)); - let val = ptr::read(self.vals().get_unchecked(idx)); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(internal) => { - let edge = ptr::read(internal.as_internal().edges.get_unchecked(idx + 1)); - let mut new_root = Root { node: edge, height: internal.height - 1 }; - new_root.as_mut().as_leaf_mut().parent = ptr::null(); - Some(new_root) - } - }; - - self.as_leaf_mut().len -= 1; - (key, val, edge) - } - } - - /// Removes a key/value pair from the beginning of this node. If this is an internal node, - /// also removes the edge that was to the left of that pair. - pub fn pop_front(&mut self) -> (K, V, Option>) { - // Necessary for correctness, but this is an internal module - debug_assert!(self.len() > 0); - - let old_len = self.len(); - - unsafe { - let key = slice_remove(self.keys_mut(), 0); - let val = slice_remove(self.vals_mut(), 0); - let edge = match self.reborrow_mut().force() { - ForceResult::Leaf(_) => None, - ForceResult::Internal(mut internal) => { - let edge = slice_remove( - slice::from_raw_parts_mut( - internal.as_internal_mut().edges.as_mut_ptr(), - old_len+1 - ), - 0 - ); - - let mut new_root = Root { node: edge, height: internal.height - 1 }; - new_root.as_mut().as_leaf_mut().parent = ptr::null(); - - for i in 0..old_len { - Handle::new_edge(internal.reborrow_mut(), i).correct_parent_link(); - } - - Some(new_root) - } - }; - - self.as_leaf_mut().len -= 1; - - (key, val, edge) - } - } - - fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { - ( - self.keys_mut().as_mut_ptr(), - self.vals_mut().as_mut_ptr() - ) - } -} - -impl NodeRef { - /// Checks whether a node is an `Internal` node or a `Leaf` node. - pub fn force(self) -> ForceResult< - NodeRef, - NodeRef - > { - if self.height == 0 { - ForceResult::Leaf(NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - }) - } else { - ForceResult::Internal(NodeRef { - height: self.height, - node: self.node, - root: self.root, - _marker: PhantomData - }) - } - } -} - -/// A reference to a specific key/value pair or edge within a node. The `Node` parameter -/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key/value -/// pair) or `Edge` (signifying a handle on an edge). -/// -/// Note that even `Leaf` nodes can have `Edge` handles. Instead of representing a pointer to -/// a child node, these represent the spaces where child pointers would go between the key/value -/// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one -/// to the left of the node, one between the two pairs, and one at the right of the node. -pub struct Handle { - node: Node, - idx: usize, - _marker: PhantomData -} - -impl Copy for Handle { } -// We don't need the full generality of `#[derive(Clone)]`, as the only time `Node` will be -// `Clone`able is when it is an immutable reference and therefore `Copy`. -impl Clone for Handle { - fn clone(&self) -> Self { - *self - } -} - -impl Handle { - /// Retrieves the node that contains the edge of key/value pair this handle points to. - pub fn into_node(self) -> Node { - self.node - } -} - -impl Handle, marker::KV> { - /// Creates a new handle to a key/value pair in `node`. `idx` must be less than `node.len()`. - pub fn new_kv(node: NodeRef, idx: usize) -> Self { - // Necessary for correctness, but in a private module - debug_assert!(idx < node.len()); - - Handle { - node, - idx, - _marker: PhantomData - } - } - - pub fn left_edge(self) -> Handle, marker::Edge> { - Handle::new_edge(self.node, self.idx) - } - - pub fn right_edge(self) -> Handle, marker::Edge> { - Handle::new_edge(self.node, self.idx + 1) - } -} - -impl PartialEq - for Handle, HandleType> { - - fn eq(&self, other: &Self) -> bool { - self.node.node == other.node.node && self.idx == other.idx - } -} - -impl - Handle, HandleType> { - - /// Temporarily takes out another, immutable handle on the same location. - pub fn reborrow(&self) - -> Handle, HandleType> { - - // We can't use Handle::new_kv or Handle::new_edge because we don't know our type - Handle { - node: self.node.reborrow(), - idx: self.idx, - _marker: PhantomData - } - } -} - -impl<'a, K, V, NodeType, HandleType> - Handle, K, V, NodeType>, HandleType> { - - /// Temporarily takes out another, mutable handle on the same location. Beware, as - /// this method is very dangerous, doubly so since it may not immediately appear - /// dangerous. - /// - /// Because mutable pointers can roam anywhere around the tree and can even (through - /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` - /// can easily be used to make the original mutable pointer dangling, or, in the case - /// of a reborrowed handle, out of bounds. - // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts - // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. - pub unsafe fn reborrow_mut(&mut self) - -> Handle, HandleType> { - - // We can't use Handle::new_kv or Handle::new_edge because we don't know our type - Handle { - node: self.node.reborrow_mut(), - idx: self.idx, - _marker: PhantomData - } - } -} - -impl - Handle, marker::Edge> { - - /// Creates a new handle to an edge in `node`. `idx` must be less than or equal to - /// `node.len()`. - pub fn new_edge(node: NodeRef, idx: usize) -> Self { - // Necessary for correctness, but in a private module - debug_assert!(idx <= node.len()); - - Handle { - node, - idx, - _marker: PhantomData - } - } - - pub fn left_kv(self) - -> Result, marker::KV>, Self> { - - if self.idx > 0 { - Ok(Handle::new_kv(self.node, self.idx - 1)) - } else { - Err(self) - } - } - - pub fn right_kv(self) - -> Result, marker::KV>, Self> { - - if self.idx < self.node.len() { - Ok(Handle::new_kv(self.node, self.idx)) - } else { - Err(self) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { - /// Inserts a new key/value pair between the key/value pairs to the right and left of - /// this edge. This method assumes that there is enough space in the node for the new - /// pair to fit. - /// - /// The returned pointer points to the inserted value. - fn insert_fit(&mut self, key: K, val: V) -> *mut V { - // Necessary for correctness, but in a private module - debug_assert!(self.node.len() < CAPACITY); - debug_assert!(!self.node.is_shared_root()); - - unsafe { - slice_insert(self.node.keys_mut(), self.idx, key); - slice_insert(self.node.vals_mut(), self.idx, val); - - self.node.as_leaf_mut().len += 1; - - self.node.vals_mut().get_unchecked_mut(self.idx) - } - } - - /// Inserts a new key/value pair between the key/value pairs to the right and left of - /// this edge. This method splits the node if there isn't enough room. - /// - /// The returned pointer points to the inserted value. - pub fn insert(mut self, key: K, val: V) - -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) { - - if self.node.len() < CAPACITY { - let ptr = self.insert_fit(key, val); - (InsertResult::Fit(Handle::new_kv(self.node, self.idx)), ptr) - } else { - let middle = Handle::new_kv(self.node, B); - let (mut left, k, v, mut right) = middle.split(); - let ptr = if self.idx <= B { - unsafe { - Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val) - } - } else { - unsafe { - Handle::new_edge( - right.as_mut().cast_unchecked::(), - self.idx - (B + 1) - ).insert_fit(key, val) - } - }; - (InsertResult::Split(left, k, v, right), ptr) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Internal>, marker::Edge> { - /// Fixes the parent pointer and index in the child node below this edge. This is useful - /// when the ordering of edges has been changed, such as in the various `insert` methods. - fn correct_parent_link(mut self) { - let idx = self.idx as u16; - let ptr = self.node.as_internal_mut() as *mut _; - let mut child = self.descend(); - child.as_leaf_mut().parent = ptr; - child.as_leaf_mut().parent_idx = idx; - } - - /// Unsafely asserts to the compiler some static information about whether the underlying - /// node of this handle is a `Leaf`. - unsafe fn cast_unchecked(&mut self) - -> Handle, marker::Edge> { - - Handle::new_edge(self.node.cast_unchecked(), self.idx) - } - - /// Inserts a new key/value pair and an edge that will go to the right of that new pair - /// between this edge and the key/value pair to the right of this edge. This method assumes - /// that there is enough space in the node for the new pair to fit. - fn insert_fit(&mut self, key: K, val: V, edge: Root) { - // Necessary for correctness, but in an internal module - debug_assert!(self.node.len() < CAPACITY); - debug_assert!(edge.height == self.node.height - 1); - - unsafe { - // This cast is a lie, but it allows us to reuse the key/value insertion logic. - self.cast_unchecked::().insert_fit(key, val); - - slice_insert( - slice::from_raw_parts_mut( - self.node.as_internal_mut().edges.as_mut_ptr(), - self.node.len() - ), - self.idx + 1, - edge.node - ); - - for i in (self.idx+1)..(self.node.len()+1) { - Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); - } - } - } - - /// Inserts a new key/value pair and an edge that will go to the right of that new pair - /// between this edge and the key/value pair to the right of this edge. This method splits - /// the node if there isn't enough room. - pub fn insert(mut self, key: K, val: V, edge: Root) - -> InsertResult<'a, K, V, marker::Internal> { - - // Necessary for correctness, but this is an internal module - debug_assert!(edge.height == self.node.height - 1); - - if self.node.len() < CAPACITY { - self.insert_fit(key, val, edge); - InsertResult::Fit(Handle::new_kv(self.node, self.idx)) - } else { - let middle = Handle::new_kv(self.node, B); - let (mut left, k, v, mut right) = middle.split(); - if self.idx <= B { - unsafe { - Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge); - } - } else { - unsafe { - Handle::new_edge( - right.as_mut().cast_unchecked::(), - self.idx - (B + 1) - ).insert_fit(key, val, edge); - } - } - InsertResult::Split(left, k, v, right) - } - } -} - -impl - Handle, marker::Edge> { - - /// Finds the node pointed to by this edge. - /// - /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should - /// both, upon success, do nothing. - pub fn descend(self) -> NodeRef { - NodeRef { - height: self.node.height - 1, - node: unsafe { self.node.as_internal().edges.get_unchecked(self.idx).as_ptr() }, - root: self.node.root, - _marker: PhantomData - } - } -} - -impl<'a, K: 'a, V: 'a, NodeType> - Handle, K, V, NodeType>, marker::KV> { - - pub fn into_kv(self) -> (&'a K, &'a V) { - let (keys, vals) = self.node.into_slices(); - unsafe { - (keys.get_unchecked(self.idx), vals.get_unchecked(self.idx)) - } - } -} - -impl<'a, K: 'a, V: 'a, NodeType> - Handle, K, V, NodeType>, marker::KV> { - - pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { - let (keys, vals) = self.node.into_slices_mut(); - unsafe { - (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) - } - } -} - -impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { - pub fn kv_mut(&mut self) -> (&mut K, &mut V) { - unsafe { - let (keys, vals) = self.node.reborrow_mut().into_slices_mut(); - (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> { - /// Splits the underlying node into three parts: - /// - /// - The node is truncated to only contain the key/value pairs to the right of - /// this handle. - /// - The key and value pointed to by this handle and extracted. - /// - All the key/value pairs to the right of this handle are put into a newly - /// allocated node. - pub fn split(mut self) - -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { - debug_assert!(!self.node.is_shared_root()); - unsafe { - let mut new_node = Box::new(LeafNode::new()); - - let k = ptr::read(self.node.keys().get_unchecked(self.idx)); - let v = ptr::read(self.node.vals().get_unchecked(self.idx)); - - let new_len = self.node.len() - self.idx - 1; - - ptr::copy_nonoverlapping( - self.node.keys().as_ptr().offset(self.idx as isize + 1), - new_node.keys.as_mut_ptr(), - new_len - ); - ptr::copy_nonoverlapping( - self.node.vals().as_ptr().offset(self.idx as isize + 1), - new_node.vals.as_mut_ptr(), - new_len - ); - - self.node.as_leaf_mut().len = self.idx as u16; - new_node.len = new_len as u16; - - ( - self.node, - k, v, - Root { - node: BoxedNode::from_leaf(new_node), - height: 0 - } - ) - } - } - - /// Removes the key/value pair pointed to by this handle, returning the edge between the - /// now adjacent key/value pairs to the left and right of this handle. - pub fn remove(mut self) - -> (Handle, K, V, marker::Leaf>, marker::Edge>, K, V) { - debug_assert!(!self.node.is_shared_root()); - unsafe { - let k = slice_remove(self.node.keys_mut(), self.idx); - let v = slice_remove(self.node.vals_mut(), self.idx); - self.node.as_leaf_mut().len -= 1; - (self.left_edge(), k, v) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { - /// Splits the underlying node into three parts: - /// - /// - The node is truncated to only contain the edges and key/value pairs to the - /// right of this handle. - /// - The key and value pointed to by this handle and extracted. - /// - All the edges and key/value pairs to the right of this handle are put into - /// a newly allocated node. - pub fn split(mut self) - -> (NodeRef, K, V, marker::Internal>, K, V, Root) { - unsafe { - let mut new_node = Box::new(InternalNode::new()); - - let k = ptr::read(self.node.keys().get_unchecked(self.idx)); - let v = ptr::read(self.node.vals().get_unchecked(self.idx)); - - let height = self.node.height; - let new_len = self.node.len() - self.idx - 1; - - ptr::copy_nonoverlapping( - self.node.keys().as_ptr().offset(self.idx as isize + 1), - new_node.data.keys.as_mut_ptr(), - new_len - ); - ptr::copy_nonoverlapping( - self.node.vals().as_ptr().offset(self.idx as isize + 1), - new_node.data.vals.as_mut_ptr(), - new_len - ); - ptr::copy_nonoverlapping( - self.node.as_internal().edges.as_ptr().offset(self.idx as isize + 1), - new_node.edges.as_mut_ptr(), - new_len + 1 - ); - - self.node.as_leaf_mut().len = self.idx as u16; - new_node.data.len = new_len as u16; - - let mut new_root = Root { - node: BoxedNode::from_internal(new_node), - height, - }; - - for i in 0..(new_len+1) { - Handle::new_edge(new_root.as_mut().cast_unchecked(), i).correct_parent_link(); - } - - ( - self.node, - k, v, - new_root - ) - } - } - - /// Returns whether it is valid to call `.merge()`, i.e., whether there is enough room in - /// a node to hold the combination of the nodes to the left and right of this handle along - /// with the key/value pair at this handle. - pub fn can_merge(&self) -> bool { - ( - self.reborrow() - .left_edge() - .descend() - .len() - + self.reborrow() - .right_edge() - .descend() - .len() - + 1 - ) <= CAPACITY - } - - /// Combines the node immediately to the left of this handle, the key/value pair pointed - /// to by this handle, and the node immediately to the right of this handle into one new - /// child of the underlying node, returning an edge referencing that new child. - /// - /// Assumes that this edge `.can_merge()`. - pub fn merge(mut self) - -> Handle, K, V, marker::Internal>, marker::Edge> { - let self1 = unsafe { ptr::read(&self) }; - let self2 = unsafe { ptr::read(&self) }; - let mut left_node = self1.left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = self2.right_edge().descend(); - let right_len = right_node.len(); - - // necessary for correctness, but in a private module - debug_assert!(left_len + right_len + 1 <= CAPACITY); - - unsafe { - ptr::write(left_node.keys_mut().get_unchecked_mut(left_len), - slice_remove(self.node.keys_mut(), self.idx)); - ptr::copy_nonoverlapping( - right_node.keys().as_ptr(), - left_node.keys_mut().as_mut_ptr().offset(left_len as isize + 1), - right_len - ); - ptr::write(left_node.vals_mut().get_unchecked_mut(left_len), - slice_remove(self.node.vals_mut(), self.idx)); - ptr::copy_nonoverlapping( - right_node.vals().as_ptr(), - left_node.vals_mut().as_mut_ptr().offset(left_len as isize + 1), - right_len - ); - - slice_remove(&mut self.node.as_internal_mut().edges, self.idx + 1); - for i in self.idx+1..self.node.len() { - Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); - } - self.node.as_leaf_mut().len -= 1; - - left_node.as_leaf_mut().len += right_len as u16 + 1; - - if self.node.height > 1 { - ptr::copy_nonoverlapping( - right_node.cast_unchecked().as_internal().edges.as_ptr(), - left_node.cast_unchecked() - .as_internal_mut() - .edges - .as_mut_ptr() - .offset(left_len as isize + 1), - right_len + 1 - ); - - for i in left_len+1..left_len+right_len+2 { - Handle::new_edge( - left_node.cast_unchecked().reborrow_mut(), - i - ).correct_parent_link(); - } - - Global.dealloc( - right_node.node.cast(), - Layout::new::>(), - ); - } else { - Global.dealloc( - right_node.node.cast(), - Layout::new::>(), - ); - } - - Handle::new_edge(self.node, self.idx) - } - } - - /// This removes a key/value pair from the left child and replaces it with the key/value pair - /// pointed to by this handle while pushing the old key/value pair of this handle into the right - /// child. - pub fn steal_left(&mut self) { - unsafe { - let (k, v, edge) = self.reborrow_mut().left_edge().descend().pop(); - - let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); - let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); - - match self.reborrow_mut().right_edge().descend().force() { - ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), - ForceResult::Internal(mut internal) => internal.push_front(k, v, edge.unwrap()) - } - } - } - - /// This removes a key/value pair from the right child and replaces it with the key/value pair - /// pointed to by this handle while pushing the old key/value pair of this handle into the left - /// child. - pub fn steal_right(&mut self) { - unsafe { - let (k, v, edge) = self.reborrow_mut().right_edge().descend().pop_front(); - - let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); - let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); - - match self.reborrow_mut().left_edge().descend().force() { - ForceResult::Leaf(mut leaf) => leaf.push(k, v), - ForceResult::Internal(mut internal) => internal.push(k, v, edge.unwrap()) - } - } - } - - /// This does stealing similar to `steal_left` but steals multiple elements at once. - pub fn bulk_steal_left(&mut self, count: usize) { - unsafe { - let mut left_node = ptr::read(self).left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = ptr::read(self).right_edge().descend(); - let right_len = right_node.len(); - - // Make sure that we may steal safely. - debug_assert!(right_len + count <= CAPACITY); - debug_assert!(left_len >= count); - - let new_left_len = left_len - count; - - // Move data. - { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.reborrow_mut().into_kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; - - // Make room for stolen elements in the right child. - ptr::copy(right_kv.0, - right_kv.0.offset(count as isize), - right_len); - ptr::copy(right_kv.1, - right_kv.1.offset(count as isize), - right_len); - - // Move elements from the left child to the right one. - move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1); - - // Move parent's key/value pair to the right child. - move_kv(parent_kv, 0, right_kv, count - 1, 1); - - // Move the left-most stolen pair to the parent. - move_kv(left_kv, new_left_len, parent_kv, 0, 1); - } - - left_node.reborrow_mut().as_leaf_mut().len -= count as u16; - right_node.reborrow_mut().as_leaf_mut().len += count as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { - // Make room for stolen edges. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); - ptr::copy(right_edges, - right_edges.offset(count as isize), - right_len + 1); - right.correct_childrens_parent_links(count, count + right_len + 1); - - move_edges(left, new_left_len + 1, right, 0, count); - }, - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } - _ => { unreachable!(); } - } - } - } - - /// The symmetric clone of `bulk_steal_left`. - pub fn bulk_steal_right(&mut self, count: usize) { - unsafe { - let mut left_node = ptr::read(self).left_edge().descend(); - let left_len = left_node.len(); - let mut right_node = ptr::read(self).right_edge().descend(); - let right_len = right_node.len(); - - // Make sure that we may steal safely. - debug_assert!(left_len + count <= CAPACITY); - debug_assert!(right_len >= count); - - let new_right_len = right_len - count; - - // Move data. - { - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - let parent_kv = { - let kv = self.reborrow_mut().into_kv_mut(); - (kv.0 as *mut K, kv.1 as *mut V) - }; - - // Move parent's key/value pair to the left child. - move_kv(parent_kv, 0, left_kv, left_len, 1); - - // Move elements from the right child to the left one. - move_kv(right_kv, 0, left_kv, left_len + 1, count - 1); - - // Move the right-most stolen pair to the parent. - move_kv(right_kv, count - 1, parent_kv, 0, 1); - - // Fix right indexing - ptr::copy(right_kv.0.offset(count as isize), - right_kv.0, - new_right_len); - ptr::copy(right_kv.1.offset(count as isize), - right_kv.1, - new_right_len); - } - - left_node.reborrow_mut().as_leaf_mut().len += count as u16; - right_node.reborrow_mut().as_leaf_mut().len -= count as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { - move_edges(right.reborrow_mut(), 0, left, left_len + 1, count); - - // Fix right indexing. - let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); - ptr::copy(right_edges.offset(count as isize), - right_edges, - new_right_len + 1); - right.correct_childrens_parent_links(0, new_right_len + 1); - }, - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } - _ => { unreachable!(); } - } - } - } -} - -unsafe fn move_kv( - source: (*mut K, *mut V), source_offset: usize, - dest: (*mut K, *mut V), dest_offset: usize, - count: usize) -{ - ptr::copy_nonoverlapping(source.0.offset(source_offset as isize), - dest.0.offset(dest_offset as isize), - count); - ptr::copy_nonoverlapping(source.1.offset(source_offset as isize), - dest.1.offset(dest_offset as isize), - count); -} - -// Source and destination must have the same height. -unsafe fn move_edges( - mut source: NodeRef, source_offset: usize, - mut dest: NodeRef, dest_offset: usize, - count: usize) -{ - let source_ptr = source.as_internal_mut().edges.as_mut_ptr(); - let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); - ptr::copy_nonoverlapping(source_ptr.offset(source_offset as isize), - dest_ptr.offset(dest_offset as isize), - count); - dest.correct_childrens_parent_links(dest_offset, dest_offset + count); -} - -impl - Handle, HandleType> { - - /// Check whether the underlying node is an `Internal` node or a `Leaf` node. - pub fn force(self) -> ForceResult< - Handle, HandleType>, - Handle, HandleType> - > { - match self.node.force() { - ForceResult::Leaf(node) => ForceResult::Leaf(Handle { - node, - idx: self.idx, - _marker: PhantomData - }), - ForceResult::Internal(node) => ForceResult::Internal(Handle { - node, - idx: self.idx, - _marker: PhantomData - }) - } - } -} - -impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { - /// Move the suffix after `self` from one node to another one. `right` must be empty. - /// The first edge of `right` remains unchanged. - pub fn move_suffix(&mut self, - right: &mut NodeRef, K, V, marker::LeafOrInternal>) { - unsafe { - let left_new_len = self.idx; - let mut left_node = self.reborrow_mut().into_node(); - - let right_new_len = left_node.len() - left_new_len; - let mut right_node = right.reborrow_mut(); - - debug_assert!(right_node.len() == 0); - debug_assert!(left_node.height == right_node.height); - - let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); - let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); - - - move_kv(left_kv, left_new_len, right_kv, 0, right_new_len); - - left_node.reborrow_mut().as_leaf_mut().len = left_new_len as u16; - right_node.reborrow_mut().as_leaf_mut().len = right_new_len as u16; - - match (left_node.force(), right_node.force()) { - (ForceResult::Internal(left), ForceResult::Internal(right)) => { - move_edges(left, left_new_len + 1, right, 1, right_new_len); - }, - (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } - _ => { unreachable!(); } - } - } - } -} - -pub enum ForceResult { - Leaf(Leaf), - Internal(Internal) -} - -pub enum InsertResult<'a, K, V, Type> { - Fit(Handle, K, V, Type>, marker::KV>), - Split(NodeRef, K, V, Type>, K, V, Root) -} - -pub mod marker { - use core::marker::PhantomData; - - pub enum Leaf { } - pub enum Internal { } - pub enum LeafOrInternal { } - - pub enum Owned { } - pub struct Immut<'a>(PhantomData<&'a ()>); - pub struct Mut<'a>(PhantomData<&'a mut ()>); - - pub enum KV { } - pub enum Edge { } -} - -unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { - ptr::copy( - slice.as_ptr().offset(idx as isize), - slice.as_mut_ptr().offset(idx as isize + 1), - slice.len() - idx - ); - ptr::write(slice.get_unchecked_mut(idx), val); -} - -unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { - let ret = ptr::read(slice.get_unchecked(idx)); - ptr::copy( - slice.as_ptr().offset(idx as isize + 1), - slice.as_mut_ptr().offset(idx as isize), - slice.len() - idx - 1 - ); - ret -} diff --git a/src/liballoc/btree/search.rs b/src/liballoc/btree/search.rs deleted file mode 100644 index bc1272fbc78..00000000000 --- a/src/liballoc/btree/search.rs +++ /dev/null @@ -1,75 +0,0 @@ -// Copyright 2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -use core::cmp::Ordering; - -use borrow::Borrow; - -use super::node::{Handle, NodeRef, marker}; - -use super::node::ForceResult::*; -use self::SearchResult::*; - -pub enum SearchResult { - Found(Handle, marker::KV>), - GoDown(Handle, marker::Edge>) -} - -pub fn search_tree( - mut node: NodeRef, - key: &Q -) -> SearchResult - where Q: Ord, K: Borrow { - - loop { - match search_node(node, key) { - Found(handle) => return Found(handle), - GoDown(handle) => match handle.force() { - Leaf(leaf) => return GoDown(leaf), - Internal(internal) => { - node = internal.descend(); - continue; - } - } - } - } -} - -pub fn search_node( - node: NodeRef, - key: &Q -) -> SearchResult - where Q: Ord, K: Borrow { - - match search_linear(&node, key) { - (idx, true) => Found( - Handle::new_kv(node, idx) - ), - (idx, false) => SearchResult::GoDown( - Handle::new_edge(node, idx) - ) - } -} - -pub fn search_linear( - node: &NodeRef, - key: &Q -) -> (usize, bool) - where Q: Ord, K: Borrow { - - for (i, k) in node.keys().iter().enumerate() { - match key.cmp(k.borrow()) { - Ordering::Greater => {}, - Ordering::Equal => return (i, true), - Ordering::Less => return (i, false) - } - } - (node.keys().len(), false) -} diff --git a/src/liballoc/btree/set.rs b/src/liballoc/btree/set.rs deleted file mode 100644 index 2aad476d315..00000000000 --- a/src/liballoc/btree/set.rs +++ /dev/null @@ -1,1153 +0,0 @@ -// Copyright 2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -// This is pretty much entirely stolen from TreeSet, since BTreeMap has an identical interface -// to TreeMap - -use core::cmp::Ordering::{self, Less, Greater, Equal}; -use core::cmp::{min, max}; -use core::fmt::Debug; -use core::fmt; -use core::iter::{Peekable, FromIterator, FusedIterator}; -use core::ops::{BitOr, BitAnd, BitXor, Sub, RangeBounds}; - -use borrow::Borrow; -use btree_map::{BTreeMap, Keys}; -use super::Recover; - -// FIXME(conventions): implement bounded iterators - -/// A set based on a B-Tree. -/// -/// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance -/// benefits and drawbacks. -/// -/// It is a logic error for an item to be modified in such a way that the item's ordering relative -/// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is -/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. -/// -/// [`BTreeMap`]: struct.BTreeMap.html -/// [`Ord`]: ../../std/cmp/trait.Ord.html -/// [`Cell`]: ../../std/cell/struct.Cell.html -/// [`RefCell`]: ../../std/cell/struct.RefCell.html -/// -/// # Examples -/// -/// ``` -/// use std::collections::BTreeSet; -/// -/// // Type inference lets us omit an explicit type signature (which -/// // would be `BTreeSet<&str>` in this example). -/// let mut books = BTreeSet::new(); -/// -/// // Add some books. -/// books.insert("A Dance With Dragons"); -/// books.insert("To Kill a Mockingbird"); -/// books.insert("The Odyssey"); -/// books.insert("The Great Gatsby"); -/// -/// // Check for a specific one. -/// if !books.contains("The Winds of Winter") { -/// println!("We have {} books, but The Winds of Winter ain't one.", -/// books.len()); -/// } -/// -/// // Remove a book. -/// books.remove("The Odyssey"); -/// -/// // Iterate over everything. -/// for book in &books { -/// println!("{}", book); -/// } -/// ``` -#[derive(Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct BTreeSet { - map: BTreeMap, -} - -/// An iterator over the items of a `BTreeSet`. -/// -/// This `struct` is created by the [`iter`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`iter`]: struct.BTreeSet.html#method.iter -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - iter: Keys<'a, T, ()>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Iter") - .field(&self.iter.clone()) - .finish() - } -} - -/// An owning iterator over the items of a `BTreeSet`. -/// -/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`][`BTreeSet`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`into_iter`]: struct.BTreeSet.html#method.into_iter -#[stable(feature = "rust1", since = "1.0.0")] -#[derive(Debug)] -pub struct IntoIter { - iter: ::btree_map::IntoIter, -} - -/// An iterator over a sub-range of items in a `BTreeSet`. -/// -/// This `struct` is created by the [`range`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`range`]: struct.BTreeSet.html#method.range -#[derive(Debug)] -#[stable(feature = "btree_range", since = "1.17.0")] -pub struct Range<'a, T: 'a> { - iter: ::btree_map::Range<'a, T, ()>, -} - -/// A lazy iterator producing elements in the difference of `BTreeSet`s. -/// -/// This `struct` is created by the [`difference`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`difference`]: struct.BTreeSet.html#method.difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Difference<'a, T: 'a> { - a: Peekable>, - b: Peekable>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Difference<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Difference") - .field(&self.a) - .field(&self.b) - .finish() - } -} - -/// A lazy iterator producing elements in the symmetric difference of `BTreeSet`s. -/// -/// This `struct` is created by the [`symmetric_difference`] method on -/// [`BTreeSet`]. See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`symmetric_difference`]: struct.BTreeSet.html#method.symmetric_difference -#[stable(feature = "rust1", since = "1.0.0")] -pub struct SymmetricDifference<'a, T: 'a> { - a: Peekable>, - b: Peekable>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for SymmetricDifference<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("SymmetricDifference") - .field(&self.a) - .field(&self.b) - .finish() - } -} - -/// A lazy iterator producing elements in the intersection of `BTreeSet`s. -/// -/// This `struct` is created by the [`intersection`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`intersection`]: struct.BTreeSet.html#method.intersection -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Intersection<'a, T: 'a> { - a: Peekable>, - b: Peekable>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Intersection<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Intersection") - .field(&self.a) - .field(&self.b) - .finish() - } -} - -/// A lazy iterator producing elements in the union of `BTreeSet`s. -/// -/// This `struct` is created by the [`union`] method on [`BTreeSet`]. -/// See its documentation for more. -/// -/// [`BTreeSet`]: struct.BTreeSet.html -/// [`union`]: struct.BTreeSet.html#method.union -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Union<'a, T: 'a> { - a: Peekable>, - b: Peekable>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Union<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Union") - .field(&self.a) - .field(&self.b) - .finish() - } -} - -impl BTreeSet { - /// Makes a new `BTreeSet` with a reasonable choice of B. - /// - /// # Examples - /// - /// ``` - /// # #![allow(unused_mut)] - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet = BTreeSet::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> BTreeSet { - BTreeSet { map: BTreeMap::new() } - } - - /// Constructs a double-ended iterator over a sub-range of elements in the set. - /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will - /// yield elements from min (inclusive) to max (exclusive). - /// The range may also be entered as `(Bound, Bound)`, so for example - /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive - /// range from 4 to 10. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// use std::ops::Bound::Included; - /// - /// let mut set = BTreeSet::new(); - /// set.insert(3); - /// set.insert(5); - /// set.insert(8); - /// for &elem in set.range((Included(&4), Included(&8))) { - /// println!("{}", elem); - /// } - /// assert_eq!(Some(&5), set.range(4..).next()); - /// ``` - #[stable(feature = "btree_range", since = "1.17.0")] - pub fn range(&self, range: R) -> Range - where K: Ord, T: Borrow, R: RangeBounds - { - Range { iter: self.map.range(range) } - } - - /// Visits the values representing the difference, - /// i.e. the values that are in `self` but not in `other`, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let diff: Vec<_> = a.difference(&b).cloned().collect(); - /// assert_eq!(diff, [1]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> { - Difference { - a: self.iter().peekable(), - b: other.iter().peekable(), - } - } - - /// Visits the values representing the symmetric difference, - /// i.e. the values that are in `self` or in `other` but not in both, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let sym_diff: Vec<_> = a.symmetric_difference(&b).cloned().collect(); - /// assert_eq!(sym_diff, [1, 3]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn symmetric_difference<'a>(&'a self, - other: &'a BTreeSet) - -> SymmetricDifference<'a, T> { - SymmetricDifference { - a: self.iter().peekable(), - b: other.iter().peekable(), - } - } - - /// Visits the values representing the intersection, - /// i.e. the values that are both in `self` and `other`, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// b.insert(3); - /// - /// let intersection: Vec<_> = a.intersection(&b).cloned().collect(); - /// assert_eq!(intersection, [2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> { - Intersection { - a: self.iter().peekable(), - b: other.iter().peekable(), - } - } - - /// Visits the values representing the union, - /// i.e. all the values in `self` or `other`, without duplicates, - /// in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(2); - /// - /// let union: Vec<_> = a.union(&b).cloned().collect(); - /// assert_eq!(union, [1, 2]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> { - Union { - a: self.iter().peekable(), - b: other.iter().peekable(), - } - } - - /// Clears the set, removing all values. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// v.insert(1); - /// v.clear(); - /// assert!(v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - self.map.clear() - } - - /// Returns `true` if the set contains a value. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.contains(&1), true); - /// assert_eq!(set.contains(&4), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn contains(&self, value: &Q) -> bool - where T: Borrow, - Q: Ord - { - self.map.contains_key(value) - } - - /// Returns a reference to the value in the set, if any, that is equal to the given value. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.get(&2), Some(&2)); - /// assert_eq!(set.get(&4), None); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn get(&self, value: &Q) -> Option<&T> - where T: Borrow, - Q: Ord - { - Recover::get(&self.map, value) - } - - /// Returns `true` if `self` has no elements in common with `other`. - /// This is equivalent to checking for an empty intersection. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut b = BTreeSet::new(); - /// - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(4); - /// assert_eq!(a.is_disjoint(&b), true); - /// b.insert(1); - /// assert_eq!(a.is_disjoint(&b), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_disjoint(&self, other: &BTreeSet) -> bool { - self.intersection(other).next().is_none() - } - - /// Returns `true` if the set is a subset of another, - /// i.e. `other` contains at least all the values in `self`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let sup: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(2); - /// assert_eq!(set.is_subset(&sup), true); - /// set.insert(4); - /// assert_eq!(set.is_subset(&sup), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_subset(&self, other: &BTreeSet) -> bool { - // Stolen from TreeMap - let mut x = self.iter(); - let mut y = other.iter(); - let mut a = x.next(); - let mut b = y.next(); - while a.is_some() { - if b.is_none() { - return false; - } - - let a1 = a.unwrap(); - let b1 = b.unwrap(); - - match b1.cmp(a1) { - Less => (), - Greater => return false, - Equal => a = x.next(), - } - - b = y.next(); - } - true - } - - /// Returns `true` if the set is a superset of another, - /// i.e. `self` contains at least all the values in `other`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let sub: BTreeSet<_> = [1, 2].iter().cloned().collect(); - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(0); - /// set.insert(1); - /// assert_eq!(set.is_superset(&sub), false); - /// - /// set.insert(2); - /// assert_eq!(set.is_superset(&sub), true); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_superset(&self, other: &BTreeSet) -> bool { - other.is_subset(self) - } - - /// Adds a value to the set. - /// - /// If the set did not have this value present, `true` is returned. - /// - /// If the set did have this value present, `false` is returned, and the - /// entry is not updated. See the [module-level documentation] for more. - /// - /// [module-level documentation]: index.html#insert-and-complex-keys - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// assert_eq!(set.insert(2), true); - /// assert_eq!(set.insert(2), false); - /// assert_eq!(set.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn insert(&mut self, value: T) -> bool { - self.map.insert(value, ()).is_none() - } - - /// Adds a value to the set, replacing the existing value, if any, that is equal to the given - /// one. Returns the replaced value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// set.insert(Vec::::new()); - /// - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); - /// set.replace(Vec::with_capacity(10)); - /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn replace(&mut self, value: T) -> Option { - Recover::replace(&mut self.map, value) - } - - /// Removes a value from the set. Returns `true` if the value was - /// present in the set. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set = BTreeSet::new(); - /// - /// set.insert(2); - /// assert_eq!(set.remove(&2), true); - /// assert_eq!(set.remove(&2), false); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, value: &Q) -> bool - where T: Borrow, - Q: Ord - { - self.map.remove(value).is_some() - } - - /// Removes and returns the value in the set, if any, that is equal to the given one. - /// - /// The value may be any borrowed form of the set's value type, - /// but the ordering on the borrowed form *must* match the - /// ordering on the value type. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); - /// assert_eq!(set.take(&2), Some(2)); - /// assert_eq!(set.take(&2), None); - /// ``` - #[stable(feature = "set_recovery", since = "1.9.0")] - pub fn take(&mut self, value: &Q) -> Option - where T: Borrow, - Q: Ord - { - Recover::take(&mut self.map, value) - } - - /// Moves all elements from `other` into `Self`, leaving `other` empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// a.insert(3); - /// - /// let mut b = BTreeSet::new(); - /// b.insert(3); - /// b.insert(4); - /// b.insert(5); - /// - /// a.append(&mut b); - /// - /// assert_eq!(a.len(), 5); - /// assert_eq!(b.len(), 0); - /// - /// assert!(a.contains(&1)); - /// assert!(a.contains(&2)); - /// assert!(a.contains(&3)); - /// assert!(a.contains(&4)); - /// assert!(a.contains(&5)); - /// ``` - #[stable(feature = "btree_append", since = "1.11.0")] - pub fn append(&mut self, other: &mut Self) { - self.map.append(&mut other.map); - } - - /// Splits the collection into two at the given key. Returns everything after the given key, - /// including the key. - /// - /// # Examples - /// - /// Basic usage: - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut a = BTreeSet::new(); - /// a.insert(1); - /// a.insert(2); - /// a.insert(3); - /// a.insert(17); - /// a.insert(41); - /// - /// let b = a.split_off(&3); - /// - /// assert_eq!(a.len(), 2); - /// assert_eq!(b.len(), 3); - /// - /// assert!(a.contains(&1)); - /// assert!(a.contains(&2)); - /// - /// assert!(b.contains(&3)); - /// assert!(b.contains(&17)); - /// assert!(b.contains(&41)); - /// ``` - #[stable(feature = "btree_split_off", since = "1.11.0")] - pub fn split_off(&mut self, key: &Q) -> Self where T: Borrow { - BTreeSet { map: self.map.split_off(key) } - } -} - -impl BTreeSet { - /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [1, 2, 3].iter().cloned().collect(); - /// let mut set_iter = set.iter(); - /// assert_eq!(set_iter.next(), Some(&1)); - /// assert_eq!(set_iter.next(), Some(&2)); - /// assert_eq!(set_iter.next(), Some(&3)); - /// assert_eq!(set_iter.next(), None); - /// ``` - /// - /// Values returned by the iterator are returned in ascending order: - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [3, 1, 2].iter().cloned().collect(); - /// let mut set_iter = set.iter(); - /// assert_eq!(set_iter.next(), Some(&1)); - /// assert_eq!(set_iter.next(), Some(&2)); - /// assert_eq!(set_iter.next(), Some(&3)); - /// assert_eq!(set_iter.next(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { iter: self.map.keys() } - } - - /// Returns the number of elements in the set. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// assert_eq!(v.len(), 0); - /// v.insert(1); - /// assert_eq!(v.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.map.len() - } - - /// Returns `true` if the set contains no elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let mut v = BTreeSet::new(); - /// assert!(v.is_empty()); - /// v.insert(1); - /// assert!(!v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.len() == 0 - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for BTreeSet { - fn from_iter>(iter: I) -> BTreeSet { - let mut set = BTreeSet::new(); - set.extend(iter); - set - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for BTreeSet { - type Item = T; - type IntoIter = IntoIter; - - /// Gets an iterator for moving out the `BTreeSet`'s contents. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let set: BTreeSet = [1, 2, 3, 4].iter().cloned().collect(); - /// - /// let v: Vec<_> = set.into_iter().collect(); - /// assert_eq!(v, [1, 2, 3, 4]); - /// ``` - fn into_iter(self) -> IntoIter { - IntoIter { iter: self.map.into_iter() } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a BTreeSet { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for BTreeSet { - #[inline] - fn extend>(&mut self, iter: Iter) { - for elem in iter { - self.insert(elem); - } - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for BTreeSet { - /// Makes an empty `BTreeSet` with a reasonable choice of B. - fn default() -> BTreeSet { - BTreeSet::new() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b, T: Ord + Clone> Sub<&'b BTreeSet> for &'a BTreeSet { - type Output = BTreeSet; - - /// Returns the difference of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let result = &a - &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 2]); - /// ``` - fn sub(self, rhs: &BTreeSet) -> BTreeSet { - self.difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b, T: Ord + Clone> BitXor<&'b BTreeSet> for &'a BTreeSet { - type Output = BTreeSet; - - /// Returns the symmetric difference of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); - /// - /// let result = &a ^ &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 4]); - /// ``` - fn bitxor(self, rhs: &BTreeSet) -> BTreeSet { - self.symmetric_difference(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b, T: Ord + Clone> BitAnd<&'b BTreeSet> for &'a BTreeSet { - type Output = BTreeSet; - - /// Returns the intersection of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); - /// - /// let result = &a & &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [2, 3]); - /// ``` - fn bitand(self, rhs: &BTreeSet) -> BTreeSet { - self.intersection(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, 'b, T: Ord + Clone> BitOr<&'b BTreeSet> for &'a BTreeSet { - type Output = BTreeSet; - - /// Returns the union of `self` and `rhs` as a new `BTreeSet`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::BTreeSet; - /// - /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); - /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); - /// - /// let result = &a | &b; - /// let result_vec: Vec<_> = result.into_iter().collect(); - /// assert_eq!(result_vec, [1, 2, 3, 4, 5]); - /// ``` - fn bitor(self, rhs: &BTreeSet) -> BTreeSet { - self.union(rhs).cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Debug for BTreeSet { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_set().entries(self.iter()).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Iter<'a, T> { - fn clone(&self) -> Iter<'a, T> { - Iter { iter: self.iter.clone() } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - self.iter.next() - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for Iter<'a, T> { - fn len(&self) -> usize { self.iter.len() } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for Iter<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - fn next(&mut self) -> Option { - self.iter.next().map(|(k, _)| k) - } - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|(k, _)| k) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn len(&self) -> usize { self.iter.len() } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, T> Clone for Range<'a, T> { - fn clone(&self) -> Range<'a, T> { - Range { iter: self.iter.clone() } - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, T> Iterator for Range<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - self.iter.next().map(|(k, _)| k) - } -} - -#[stable(feature = "btree_range", since = "1.17.0")] -impl<'a, T> DoubleEndedIterator for Range<'a, T> { - fn next_back(&mut self) -> Option<&'a T> { - self.iter.next_back().map(|(k, _)| k) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for Range<'a, T> {} - -/// Compare `x` and `y`, but return `short` if x is None and `long` if y is None -fn cmp_opt(x: Option<&T>, y: Option<&T>, short: Ordering, long: Ordering) -> Ordering { - match (x, y) { - (None, _) => short, - (_, None) => long, - (Some(x1), Some(y1)) => x1.cmp(y1), - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Difference<'a, T> { - fn clone(&self) -> Difference<'a, T> { - Difference { - a: self.a.clone(), - b: self.b.clone(), - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Difference<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - loop { - match cmp_opt(self.a.peek(), self.b.peek(), Less, Less) { - Less => return self.a.next(), - Equal => { - self.a.next(); - self.b.next(); - } - Greater => { - self.b.next(); - } - } - } - } - - fn size_hint(&self) -> (usize, Option) { - let a_len = self.a.len(); - let b_len = self.b.len(); - (a_len.saturating_sub(b_len), Some(a_len)) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: Ord> FusedIterator for Difference<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for SymmetricDifference<'a, T> { - fn clone(&self) -> SymmetricDifference<'a, T> { - SymmetricDifference { - a: self.a.clone(), - b: self.b.clone(), - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - loop { - match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { - Less => return self.a.next(), - Equal => { - self.a.next(); - self.b.next(); - } - Greater => return self.b.next(), - } - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.a.len() + self.b.len())) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: Ord> FusedIterator for SymmetricDifference<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Intersection<'a, T> { - fn clone(&self) -> Intersection<'a, T> { - Intersection { - a: self.a.clone(), - b: self.b.clone(), - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Intersection<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - loop { - match Ord::cmp(self.a.peek()?, self.b.peek()?) { - Less => { - self.a.next(); - } - Equal => { - self.b.next(); - return self.a.next(); - } - Greater => { - self.b.next(); - } - } - } - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(min(self.a.len(), self.b.len()))) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: Ord> FusedIterator for Intersection<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Union<'a, T> { - fn clone(&self) -> Union<'a, T> { - Union { - a: self.a.clone(), - b: self.b.clone(), - } - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T: Ord> Iterator for Union<'a, T> { - type Item = &'a T; - - fn next(&mut self) -> Option<&'a T> { - match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { - Less => self.a.next(), - Equal => { - self.b.next(); - self.a.next() - } - Greater => self.b.next(), - } - } - - fn size_hint(&self) -> (usize, Option) { - let a_len = self.a.len(); - let b_len = self.b.len(); - (max(a_len, b_len), Some(a_len + b_len)) - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: Ord> FusedIterator for Union<'a, T> {} diff --git a/src/liballoc/collections/binary_heap.rs b/src/liballoc/collections/binary_heap.rs new file mode 100644 index 00000000000..fcadcb544c4 --- /dev/null +++ b/src/liballoc/collections/binary_heap.rs @@ -0,0 +1,1196 @@ +// Copyright 2013-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A priority queue implemented with a binary heap. +//! +//! Insertion and popping the largest element have `O(log n)` time complexity. +//! Checking the largest element is `O(1)`. Converting a vector to a binary heap +//! can be done in-place, and has `O(n)` complexity. A binary heap can also be +//! converted to a sorted vector in-place, allowing it to be used for an `O(n +//! log n)` in-place heapsort. +//! +//! # Examples +//! +//! This is a larger example that implements [Dijkstra's algorithm][dijkstra] +//! to solve the [shortest path problem][sssp] on a [directed graph][dir_graph]. +//! It shows how to use [`BinaryHeap`] with custom types. +//! +//! [dijkstra]: http://en.wikipedia.org/wiki/Dijkstra%27s_algorithm +//! [sssp]: http://en.wikipedia.org/wiki/Shortest_path_problem +//! [dir_graph]: http://en.wikipedia.org/wiki/Directed_graph +//! [`BinaryHeap`]: struct.BinaryHeap.html +//! +//! ``` +//! use std::cmp::Ordering; +//! use std::collections::BinaryHeap; +//! use std::usize; +//! +//! #[derive(Copy, Clone, Eq, PartialEq)] +//! struct State { +//! cost: usize, +//! position: usize, +//! } +//! +//! // The priority queue depends on `Ord`. +//! // Explicitly implement the trait so the queue becomes a min-heap +//! // instead of a max-heap. +//! impl Ord for State { +//! fn cmp(&self, other: &State) -> Ordering { +//! // Notice that the we flip the ordering on costs. +//! // In case of a tie we compare positions - this step is necessary +//! // to make implementations of `PartialEq` and `Ord` consistent. +//! other.cost.cmp(&self.cost) +//! .then_with(|| self.position.cmp(&other.position)) +//! } +//! } +//! +//! // `PartialOrd` needs to be implemented as well. +//! impl PartialOrd for State { +//! fn partial_cmp(&self, other: &State) -> Option { +//! Some(self.cmp(other)) +//! } +//! } +//! +//! // Each node is represented as an `usize`, for a shorter implementation. +//! struct Edge { +//! node: usize, +//! cost: usize, +//! } +//! +//! // Dijkstra's shortest path algorithm. +//! +//! // Start at `start` and use `dist` to track the current shortest distance +//! // to each node. This implementation isn't memory-efficient as it may leave duplicate +//! // nodes in the queue. It also uses `usize::MAX` as a sentinel value, +//! // for a simpler implementation. +//! fn shortest_path(adj_list: &Vec>, start: usize, goal: usize) -> Option { +//! // dist[node] = current shortest distance from `start` to `node` +//! let mut dist: Vec<_> = (0..adj_list.len()).map(|_| usize::MAX).collect(); +//! +//! let mut heap = BinaryHeap::new(); +//! +//! // We're at `start`, with a zero cost +//! dist[start] = 0; +//! heap.push(State { cost: 0, position: start }); +//! +//! // Examine the frontier with lower cost nodes first (min-heap) +//! while let Some(State { cost, position }) = heap.pop() { +//! // Alternatively we could have continued to find all shortest paths +//! if position == goal { return Some(cost); } +//! +//! // Important as we may have already found a better way +//! if cost > dist[position] { continue; } +//! +//! // For each node we can reach, see if we can find a way with +//! // a lower cost going through this node +//! for edge in &adj_list[position] { +//! let next = State { cost: cost + edge.cost, position: edge.node }; +//! +//! // If so, add it to the frontier and continue +//! if next.cost < dist[next.position] { +//! heap.push(next); +//! // Relaxation, we have now found a better way +//! dist[next.position] = next.cost; +//! } +//! } +//! } +//! +//! // Goal not reachable +//! None +//! } +//! +//! fn main() { +//! // This is the directed graph we're going to use. +//! // The node numbers correspond to the different states, +//! // and the edge weights symbolize the cost of moving +//! // from one node to another. +//! // Note that the edges are one-way. +//! // +//! // 7 +//! // +-----------------+ +//! // | | +//! // v 1 2 | 2 +//! // 0 -----> 1 -----> 3 ---> 4 +//! // | ^ ^ ^ +//! // | | 1 | | +//! // | | | 3 | 1 +//! // +------> 2 -------+ | +//! // 10 | | +//! // +---------------+ +//! // +//! // The graph is represented as an adjacency list where each index, +//! // corresponding to a node value, has a list of outgoing edges. +//! // Chosen for its efficiency. +//! let graph = vec![ +//! // Node 0 +//! vec![Edge { node: 2, cost: 10 }, +//! Edge { node: 1, cost: 1 }], +//! // Node 1 +//! vec![Edge { node: 3, cost: 2 }], +//! // Node 2 +//! vec![Edge { node: 1, cost: 1 }, +//! Edge { node: 3, cost: 3 }, +//! Edge { node: 4, cost: 1 }], +//! // Node 3 +//! vec![Edge { node: 0, cost: 7 }, +//! Edge { node: 4, cost: 2 }], +//! // Node 4 +//! vec![]]; +//! +//! assert_eq!(shortest_path(&graph, 0, 1), Some(1)); +//! assert_eq!(shortest_path(&graph, 0, 3), Some(3)); +//! assert_eq!(shortest_path(&graph, 3, 0), Some(7)); +//! assert_eq!(shortest_path(&graph, 0, 4), Some(5)); +//! assert_eq!(shortest_path(&graph, 4, 0), None); +//! } +//! ``` + +#![allow(missing_docs)] +#![stable(feature = "rust1", since = "1.0.0")] + +use core::ops::{Deref, DerefMut}; +use core::iter::{FromIterator, FusedIterator}; +use core::mem::{swap, size_of, ManuallyDrop}; +use core::ptr; +use core::fmt; + +use slice; +use vec::{self, Vec}; + +use super::SpecExtend; + +/// A priority queue implemented with a binary heap. +/// +/// This will be a max-heap. +/// +/// It is a logic error for an item to be modified in such a way that the +/// item's ordering relative to any other item, as determined by the `Ord` +/// trait, changes while it is in the heap. This is normally only possible +/// through `Cell`, `RefCell`, global state, I/O, or unsafe code. +/// +/// # Examples +/// +/// ``` +/// use std::collections::BinaryHeap; +/// +/// // Type inference lets us omit an explicit type signature (which +/// // would be `BinaryHeap` in this example). +/// let mut heap = BinaryHeap::new(); +/// +/// // We can use peek to look at the next item in the heap. In this case, +/// // there's no items in there yet so we get None. +/// assert_eq!(heap.peek(), None); +/// +/// // Let's add some scores... +/// heap.push(1); +/// heap.push(5); +/// heap.push(2); +/// +/// // Now peek shows the most important item in the heap. +/// assert_eq!(heap.peek(), Some(&5)); +/// +/// // We can check the length of a heap. +/// assert_eq!(heap.len(), 3); +/// +/// // We can iterate over the items in the heap, although they are returned in +/// // a random order. +/// for x in &heap { +/// println!("{}", x); +/// } +/// +/// // If we instead pop these scores, they should come back in order. +/// assert_eq!(heap.pop(), Some(5)); +/// assert_eq!(heap.pop(), Some(2)); +/// assert_eq!(heap.pop(), Some(1)); +/// assert_eq!(heap.pop(), None); +/// +/// // We can clear the heap of any remaining items. +/// heap.clear(); +/// +/// // The heap should now be empty. +/// assert!(heap.is_empty()) +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BinaryHeap { + data: Vec, +} + +/// Structure wrapping a mutable reference to the greatest item on a +/// `BinaryHeap`. +/// +/// This `struct` is created by the [`peek_mut`] method on [`BinaryHeap`]. See +/// its documentation for more. +/// +/// [`peek_mut`]: struct.BinaryHeap.html#method.peek_mut +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +pub struct PeekMut<'a, T: 'a + Ord> { + heap: &'a mut BinaryHeap, + sift: bool, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: Ord + fmt::Debug> fmt::Debug for PeekMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("PeekMut") + .field(&self.heap.data[0]) + .finish() + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl<'a, T: Ord> Drop for PeekMut<'a, T> { + fn drop(&mut self) { + if self.sift { + self.heap.sift_down(0); + } + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl<'a, T: Ord> Deref for PeekMut<'a, T> { + type Target = T; + fn deref(&self) -> &T { + &self.heap.data[0] + } +} + +#[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] +impl<'a, T: Ord> DerefMut for PeekMut<'a, T> { + fn deref_mut(&mut self) -> &mut T { + &mut self.heap.data[0] + } +} + +impl<'a, T: Ord> PeekMut<'a, T> { + /// Removes the peeked value from the heap and returns it. + #[stable(feature = "binary_heap_peek_mut_pop", since = "1.18.0")] + pub fn pop(mut this: PeekMut<'a, T>) -> T { + let value = this.heap.pop().unwrap(); + this.sift = false; + value + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for BinaryHeap { + fn clone(&self) -> Self { + BinaryHeap { data: self.data.clone() } + } + + fn clone_from(&mut self, source: &Self) { + self.data.clone_from(&source.data); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BinaryHeap { + /// Creates an empty `BinaryHeap`. + #[inline] + fn default() -> BinaryHeap { + BinaryHeap::new() + } +} + +#[stable(feature = "binaryheap_debug", since = "1.4.0")] +impl fmt::Debug for BinaryHeap { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self.iter()).finish() + } +} + +impl BinaryHeap { + /// Creates an empty `BinaryHeap` as a max-heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> BinaryHeap { + BinaryHeap { data: vec![] } + } + + /// Creates an empty `BinaryHeap` with a specific capacity. + /// This preallocates enough memory for `capacity` elements, + /// so that the `BinaryHeap` does not have to be reallocated + /// until it contains at least that many values. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity(10); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(capacity: usize) -> BinaryHeap { + BinaryHeap { data: Vec::with_capacity(capacity) } + } + + /// Returns an iterator visiting all values in the underlying vector, in + /// arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// + /// // Print 1, 2, 3, 4 in arbitrary order + /// for x in heap.iter() { + /// println!("{}", x); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { iter: self.data.iter() } + } + + /// Returns the greatest item in the binary heap, or `None` if it is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert_eq!(heap.peek(), None); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// assert_eq!(heap.peek(), Some(&5)); + /// + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn peek(&self) -> Option<&T> { + self.data.get(0) + } + + /// Returns a mutable reference to the greatest item in the binary heap, or + /// `None` if it is empty. + /// + /// Note: If the `PeekMut` value is leaked, the heap may be in an + /// inconsistent state. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// assert!(heap.peek_mut().is_none()); + /// + /// heap.push(1); + /// heap.push(5); + /// heap.push(2); + /// { + /// let mut val = heap.peek_mut().unwrap(); + /// *val = 0; + /// } + /// assert_eq!(heap.peek(), Some(&2)); + /// ``` + #[stable(feature = "binary_heap_peek_mut", since = "1.12.0")] + pub fn peek_mut(&mut self) -> Option> { + if self.is_empty() { + None + } else { + Some(PeekMut { + heap: self, + sift: true, + }) + } + } + + /// Returns the number of elements the binary heap can hold without reallocating. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::with_capacity(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.data.capacity() + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `BinaryHeap`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future + /// insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve_exact(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + /// + /// [`reserve`]: #method.reserve + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.data.reserve_exact(additional); + } + + /// Reserves capacity for at least `additional` more elements to be inserted in the + /// `BinaryHeap`. The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.reserve(100); + /// assert!(heap.capacity() >= 100); + /// heap.push(4); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + self.data.reserve(additional); + } + + /// Discards as much additional capacity as possible. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to_fit(); + /// assert!(heap.capacity() == 0); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn shrink_to_fit(&mut self) { + self.data.shrink_to_fit(); + } + + /// Discards capacity with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::BinaryHeap; + /// let mut heap: BinaryHeap = BinaryHeap::with_capacity(100); + /// + /// assert!(heap.capacity() >= 100); + /// heap.shrink_to(10); + /// assert!(heap.capacity() >= 10); + /// ``` + #[inline] + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + self.data.shrink_to(min_capacity) + } + + /// Removes the greatest item from the binary heap and returns it, or `None` if it + /// is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert_eq!(heap.pop(), Some(3)); + /// assert_eq!(heap.pop(), Some(1)); + /// assert_eq!(heap.pop(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop(&mut self) -> Option { + self.data.pop().map(|mut item| { + if !self.is_empty() { + swap(&mut item, &mut self.data[0]); + self.sift_down_to_bottom(0); + } + item + }) + } + + /// Pushes an item onto the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// heap.push(3); + /// heap.push(5); + /// heap.push(1); + /// + /// assert_eq!(heap.len(), 3); + /// assert_eq!(heap.peek(), Some(&5)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push(&mut self, item: T) { + let old_len = self.len(); + self.data.push(item); + self.sift_up(0, old_len); + } + + /// Consumes the `BinaryHeap` and returns the underlying vector + /// in arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4, 5, 6, 7]); + /// let vec = heap.into_vec(); + /// + /// // Will print in some order + /// for x in vec { + /// println!("{}", x); + /// } + /// ``` + #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] + pub fn into_vec(self) -> Vec { + self.into() + } + + /// Consumes the `BinaryHeap` and returns a vector in sorted + /// (ascending) order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// + /// let mut heap = BinaryHeap::from(vec![1, 2, 4, 5, 7]); + /// heap.push(6); + /// heap.push(3); + /// + /// let vec = heap.into_sorted_vec(); + /// assert_eq!(vec, [1, 2, 3, 4, 5, 6, 7]); + /// ``` + #[stable(feature = "binary_heap_extras_15", since = "1.5.0")] + pub fn into_sorted_vec(mut self) -> Vec { + let mut end = self.len(); + while end > 1 { + end -= 1; + self.data.swap(0, end); + self.sift_down_range(0, end); + } + self.into_vec() + } + + // The implementations of sift_up and sift_down use unsafe blocks in + // order to move an element out of the vector (leaving behind a + // hole), shift along the others and move the removed element back into the + // vector at the final location of the hole. + // The `Hole` type is used to represent this, and make sure + // the hole is filled back at the end of its scope, even on panic. + // Using a hole reduces the constant factor compared to using swaps, + // which involves twice as many moves. + fn sift_up(&mut self, start: usize, pos: usize) -> usize { + unsafe { + // Take out the value at `pos` and create a hole. + let mut hole = Hole::new(&mut self.data, pos); + + while hole.pos() > start { + let parent = (hole.pos() - 1) / 2; + if hole.element() <= hole.get(parent) { + break; + } + hole.move_to(parent); + } + hole.pos() + } + } + + /// Take an element at `pos` and move it down the heap, + /// while its children are larger. + fn sift_down_range(&mut self, pos: usize, end: usize) { + unsafe { + let mut hole = Hole::new(&mut self.data, pos); + let mut child = 2 * pos + 1; + while child < end { + let right = child + 1; + // compare with the greater of the two children + if right < end && !(hole.get(child) > hole.get(right)) { + child = right; + } + // if we are already in order, stop. + if hole.element() >= hole.get(child) { + break; + } + hole.move_to(child); + child = 2 * hole.pos() + 1; + } + } + } + + fn sift_down(&mut self, pos: usize) { + let len = self.len(); + self.sift_down_range(pos, len); + } + + /// Take an element at `pos` and move it all the way down the heap, + /// then sift it up to its position. + /// + /// Note: This is faster when the element is known to be large / should + /// be closer to the bottom. + fn sift_down_to_bottom(&mut self, mut pos: usize) { + let end = self.len(); + let start = pos; + unsafe { + let mut hole = Hole::new(&mut self.data, pos); + let mut child = 2 * pos + 1; + while child < end { + let right = child + 1; + // compare with the greater of the two children + if right < end && !(hole.get(child) > hole.get(right)) { + child = right; + } + hole.move_to(child); + child = 2 * hole.pos() + 1; + } + pos = hole.pos; + } + self.sift_up(start, pos); + } + + /// Returns the length of the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert_eq!(heap.len(), 2); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.data.len() + } + + /// Checks if the binary heap is empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::new(); + /// + /// assert!(heap.is_empty()); + /// + /// heap.push(3); + /// heap.push(5); + /// heap.push(1); + /// + /// assert!(!heap.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } + + /// Clears the binary heap, returning an iterator over the removed elements. + /// + /// The elements are removed in arbitrary order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// for x in heap.drain() { + /// println!("{}", x); + /// } + /// + /// assert!(heap.is_empty()); + /// ``` + #[inline] + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self) -> Drain { + Drain { iter: self.data.drain(..) } + } + + /// Drops all items from the binary heap. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let mut heap = BinaryHeap::from(vec![1, 3]); + /// + /// assert!(!heap.is_empty()); + /// + /// heap.clear(); + /// + /// assert!(heap.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.drain(); + } + + fn rebuild(&mut self) { + let mut n = self.len() / 2; + while n > 0 { + n -= 1; + self.sift_down(n); + } + } + + /// Moves all the elements of `other` into `self`, leaving `other` empty. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// + /// let v = vec![-10, 1, 2, 3, 3]; + /// let mut a = BinaryHeap::from(v); + /// + /// let v = vec![-20, 5, 43]; + /// let mut b = BinaryHeap::from(v); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.into_sorted_vec(), [-20, -10, 1, 2, 3, 3, 5, 43]); + /// assert!(b.is_empty()); + /// ``` + #[stable(feature = "binary_heap_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + if self.len() < other.len() { + swap(self, other); + } + + if other.is_empty() { + return; + } + + #[inline(always)] + fn log2_fast(x: usize) -> usize { + 8 * size_of::() - (x.leading_zeros() as usize) - 1 + } + + // `rebuild` takes O(len1 + len2) operations + // and about 2 * (len1 + len2) comparisons in the worst case + // while `extend` takes O(len2 * log_2(len1)) operations + // and about 1 * len2 * log_2(len1) comparisons in the worst case, + // assuming len1 >= len2. + #[inline] + fn better_to_rebuild(len1: usize, len2: usize) -> bool { + 2 * (len1 + len2) < len2 * log2_fast(len1) + } + + if better_to_rebuild(self.len(), other.len()) { + self.data.append(&mut other.data); + self.rebuild(); + } else { + self.extend(other.drain()); + } + } +} + +/// Hole represents a hole in a slice i.e. an index without valid value +/// (because it was moved from or duplicated). +/// In drop, `Hole` will restore the slice by filling the hole +/// position with the value that was originally removed. +struct Hole<'a, T: 'a> { + data: &'a mut [T], + elt: ManuallyDrop, + pos: usize, +} + +impl<'a, T> Hole<'a, T> { + /// Create a new Hole at index `pos`. + /// + /// Unsafe because pos must be within the data slice. + #[inline] + unsafe fn new(data: &'a mut [T], pos: usize) -> Self { + debug_assert!(pos < data.len()); + let elt = ptr::read(&data[pos]); + Hole { + data, + elt: ManuallyDrop::new(elt), + pos, + } + } + + #[inline] + fn pos(&self) -> usize { + self.pos + } + + /// Returns a reference to the element removed. + #[inline] + fn element(&self) -> &T { + &self.elt + } + + /// Returns a reference to the element at `index`. + /// + /// Unsafe because index must be within the data slice and not equal to pos. + #[inline] + unsafe fn get(&self, index: usize) -> &T { + debug_assert!(index != self.pos); + debug_assert!(index < self.data.len()); + self.data.get_unchecked(index) + } + + /// Move hole to new location + /// + /// Unsafe because index must be within the data slice and not equal to pos. + #[inline] + unsafe fn move_to(&mut self, index: usize) { + debug_assert!(index != self.pos); + debug_assert!(index < self.data.len()); + let index_ptr: *const _ = self.data.get_unchecked(index); + let hole_ptr = self.data.get_unchecked_mut(self.pos); + ptr::copy_nonoverlapping(index_ptr, hole_ptr, 1); + self.pos = index; + } +} + +impl<'a, T> Drop for Hole<'a, T> { + #[inline] + fn drop(&mut self) { + // fill the hole again + unsafe { + let pos = self.pos; + ptr::copy_nonoverlapping(&*self.elt, self.data.get_unchecked_mut(pos), 1); + } + } +} + +/// An iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`iter`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`iter`]: struct.BinaryHeap.html#method.iter +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + iter: slice::Iter<'a, T>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Iter") + .field(&self.iter.as_slice()) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Iter<'a, T> { + fn clone(&self) -> Iter<'a, T> { + Iter { iter: self.iter.clone() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for Iter<'a, T> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for Iter<'a, T> {} + +/// An owning iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BinaryHeap`][`BinaryHeap`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: struct.BinaryHeap.html#method.into_iter +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Clone)] +pub struct IntoIter { + iter: vec::IntoIter, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("IntoIter") + .field(&self.iter.as_slice()) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +/// A draining iterator over the elements of a `BinaryHeap`. +/// +/// This `struct` is created by the [`drain`] method on [`BinaryHeap`]. See its +/// documentation for more. +/// +/// [`drain`]: struct.BinaryHeap.html#method.drain +/// [`BinaryHeap`]: struct.BinaryHeap.html +#[stable(feature = "drain", since = "1.6.0")] +#[derive(Debug)] +pub struct Drain<'a, T: 'a> { + iter: vec::Drain<'a, T>, +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> Iterator for Drain<'a, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> { + fn is_empty(&self) -> bool { + self.iter.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} + +#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] +impl From> for BinaryHeap { + fn from(vec: Vec) -> BinaryHeap { + let mut heap = BinaryHeap { data: vec }; + heap.rebuild(); + heap + } +} + +#[stable(feature = "binary_heap_extras_15", since = "1.5.0")] +impl From> for Vec { + fn from(heap: BinaryHeap) -> Vec { + heap.data + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for BinaryHeap { + fn from_iter>(iter: I) -> BinaryHeap { + BinaryHeap::from(iter.into_iter().collect::>()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BinaryHeap { + type Item = T; + type IntoIter = IntoIter; + + /// Creates a consuming iterator, that is, one that moves each value out of + /// the binary heap in arbitrary order. The binary heap cannot be used + /// after calling this. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BinaryHeap; + /// let heap = BinaryHeap::from(vec![1, 2, 3, 4]); + /// + /// // Print 1, 2, 3, 4 in arbitrary order + /// for x in heap.into_iter() { + /// // x has type i32, not &i32 + /// println!("{}", x); + /// } + /// ``` + fn into_iter(self) -> IntoIter { + IntoIter { iter: self.data.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a BinaryHeap + where T: Ord +{ + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for BinaryHeap { + #[inline] + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } +} + +impl> SpecExtend for BinaryHeap { + default fn spec_extend(&mut self, iter: I) { + self.extend_desugared(iter.into_iter()); + } +} + +impl SpecExtend> for BinaryHeap { + fn spec_extend(&mut self, ref mut other: BinaryHeap) { + self.append(other); + } +} + +impl BinaryHeap { + fn extend_desugared>(&mut self, iter: I) { + let iterator = iter.into_iter(); + let (lower, _) = iterator.size_hint(); + + self.reserve(lower); + + for elem in iterator { + self.push(elem); + } + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BinaryHeap { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } +} diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs new file mode 100644 index 00000000000..e6e454446e2 --- /dev/null +++ b/src/liballoc/collections/btree/map.rs @@ -0,0 +1,2578 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp::Ordering; +use core::fmt::Debug; +use core::hash::{Hash, Hasher}; +use core::iter::{FromIterator, Peekable, FusedIterator}; +use core::marker::PhantomData; +use core::ops::Bound::{Excluded, Included, Unbounded}; +use core::ops::Index; +use core::ops::RangeBounds; +use core::{fmt, intrinsics, mem, ptr}; + +use borrow::Borrow; + +use super::node::{self, Handle, NodeRef, marker}; +use super::search; + +use super::node::InsertResult::*; +use super::node::ForceResult::*; +use super::search::SearchResult::*; +use self::UnderflowResult::*; +use self::Entry::*; + +/// A map based on a B-Tree. +/// +/// B-Trees represent a fundamental compromise between cache-efficiency and actually minimizing +/// the amount of work performed in a search. In theory, a binary search tree (BST) is the optimal +/// choice for a sorted map, as a perfectly balanced BST performs the theoretical minimum amount of +/// comparisons necessary to find an element (log2n). However, in practice the way this +/// is done is *very* inefficient for modern computer architectures. In particular, every element +/// is stored in its own individually heap-allocated node. This means that every single insertion +/// triggers a heap-allocation, and every single comparison should be a cache-miss. Since these +/// are both notably expensive things to do in practice, we are forced to at very least reconsider +/// the BST strategy. +/// +/// A B-Tree instead makes each node contain B-1 to 2B-1 elements in a contiguous array. By doing +/// this, we reduce the number of allocations by a factor of B, and improve cache efficiency in +/// searches. However, this does mean that searches will have to do *more* comparisons on average. +/// The precise number of comparisons depends on the node search strategy used. For optimal cache +/// efficiency, one could search the nodes linearly. For optimal comparisons, one could search +/// the node using binary search. As a compromise, one could also perform a linear search +/// that initially only checks every ith element for some choice of i. +/// +/// Currently, our implementation simply performs naive linear search. This provides excellent +/// performance on *small* nodes of elements which are cheap to compare. However in the future we +/// would like to further explore choosing the optimal search strategy based on the choice of B, +/// and possibly other factors. Using linear search, searching for a random element is expected +/// to take O(B logBn) comparisons, which is generally worse than a BST. In practice, +/// however, performance is excellent. +/// +/// It is a logic error for a key to be modified in such a way that the key's ordering relative to +/// any other key, as determined by the [`Ord`] trait, changes while it is in the map. This is +/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// +/// [`Ord`]: ../../std/cmp/trait.Ord.html +/// [`Cell`]: ../../std/cell/struct.Cell.html +/// [`RefCell`]: ../../std/cell/struct.RefCell.html +/// +/// # Examples +/// +/// ``` +/// use std::collections::BTreeMap; +/// +/// // type inference lets us omit an explicit type signature (which +/// // would be `BTreeMap<&str, &str>` in this example). +/// let mut movie_reviews = BTreeMap::new(); +/// +/// // review some movies. +/// movie_reviews.insert("Office Space", "Deals with real issues in the workplace."); +/// movie_reviews.insert("Pulp Fiction", "Masterpiece."); +/// movie_reviews.insert("The Godfather", "Very enjoyable."); +/// movie_reviews.insert("The Blues Brothers", "Eye lyked it alot."); +/// +/// // check for a specific one. +/// if !movie_reviews.contains_key("Les Misérables") { +/// println!("We've got {} reviews, but Les Misérables ain't one.", +/// movie_reviews.len()); +/// } +/// +/// // oops, this review has a lot of spelling mistakes, let's delete it. +/// movie_reviews.remove("The Blues Brothers"); +/// +/// // look up the values associated with some keys. +/// let to_find = ["Up!", "Office Space"]; +/// for book in &to_find { +/// match movie_reviews.get(book) { +/// Some(review) => println!("{}: {}", book, review), +/// None => println!("{} is unreviewed.", book) +/// } +/// } +/// +/// // iterate over everything. +/// for (movie, review) in &movie_reviews { +/// println!("{}: \"{}\"", movie, review); +/// } +/// ``` +/// +/// `BTreeMap` also implements an [`Entry API`](#method.entry), which allows +/// for more complex methods of getting, setting, updating and removing keys and +/// their values: +/// +/// ``` +/// use std::collections::BTreeMap; +/// +/// // type inference lets us omit an explicit type signature (which +/// // would be `BTreeMap<&str, u8>` in this example). +/// let mut player_stats = BTreeMap::new(); +/// +/// fn random_stat_buff() -> u8 { +/// // could actually return some random value here - let's just return +/// // some fixed value for now +/// 42 +/// } +/// +/// // insert a key only if it doesn't already exist +/// player_stats.entry("health").or_insert(100); +/// +/// // insert a key using a function that provides a new value only if it +/// // doesn't already exist +/// player_stats.entry("defence").or_insert_with(random_stat_buff); +/// +/// // update a key, guarding against the key possibly not being set +/// let stat = player_stats.entry("attack").or_insert(100); +/// *stat += random_stat_buff(); +/// ``` +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BTreeMap { + root: node::Root, + length: usize, +} + +#[stable(feature = "btree_drop", since = "1.7.0")] +unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { + fn drop(&mut self) { + unsafe { + drop(ptr::read(self).into_iter()); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for BTreeMap { + fn clone(&self) -> BTreeMap { + fn clone_subtree(node: node::NodeRef) + -> BTreeMap { + + match node.force() { + Leaf(leaf) => { + let mut out_tree = BTreeMap { + root: node::Root::new_leaf(), + length: 0, + }; + + { + let mut out_node = match out_tree.root.as_mut().force() { + Leaf(leaf) => leaf, + Internal(_) => unreachable!(), + }; + + let mut in_edge = leaf.first_edge(); + while let Ok(kv) = in_edge.right_kv() { + let (k, v) = kv.into_kv(); + in_edge = kv.right_edge(); + + out_node.push(k.clone(), v.clone()); + out_tree.length += 1; + } + } + + out_tree + } + Internal(internal) => { + let mut out_tree = clone_subtree(internal.first_edge().descend()); + + { + let mut out_node = out_tree.root.push_level(); + let mut in_edge = internal.first_edge(); + while let Ok(kv) = in_edge.right_kv() { + let (k, v) = kv.into_kv(); + in_edge = kv.right_edge(); + + let k = (*k).clone(); + let v = (*v).clone(); + let subtree = clone_subtree(in_edge.descend()); + + // We can't destructure subtree directly + // because BTreeMap implements Drop + let (subroot, sublength) = unsafe { + let root = ptr::read(&subtree.root); + let length = subtree.length; + mem::forget(subtree); + (root, length) + }; + + out_node.push(k, v, subroot); + out_tree.length += 1 + sublength; + } + } + + out_tree + } + } + } + + clone_subtree(self.root.as_ref()) + } +} + +impl super::Recover for BTreeMap + where K: Borrow + Ord, + Q: Ord +{ + type Key = K; + + fn get(&self, key: &Q) -> Option<&K> { + match search::search_tree(self.root.as_ref(), key) { + Found(handle) => Some(handle.into_kv().0), + GoDown(_) => None, + } + } + + fn take(&mut self, key: &Q) -> Option { + match search::search_tree(self.root.as_mut(), key) { + Found(handle) => { + Some(OccupiedEntry { + handle, + length: &mut self.length, + _marker: PhantomData, + } + .remove_kv() + .0) + } + GoDown(_) => None, + } + } + + fn replace(&mut self, key: K) -> Option { + self.ensure_root_is_owned(); + match search::search_tree::(self.root.as_mut(), &key) { + Found(handle) => Some(mem::replace(handle.into_kv_mut().0, key)), + GoDown(handle) => { + VacantEntry { + key, + handle, + length: &mut self.length, + _marker: PhantomData, + } + .insert(()); + None + } + } + } +} + +/// An iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`iter`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`iter`]: struct.BTreeMap.html#method.iter +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, K: 'a, V: 'a> { + range: Range<'a, K, V>, + length: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Iter<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: struct.BTreeMap.html#method.iter_mut +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct IterMut<'a, K: 'a, V: 'a> { + range: RangeMut<'a, K, V>, + length: usize, +} + +/// An owning iterator over the entries of a `BTreeMap`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BTreeMap`][`BTreeMap`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: struct.BTreeMap.html#method.into_iter +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + front: Handle, marker::Edge>, + back: Handle, marker::Edge>, + length: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let range = Range { + front: self.front.reborrow(), + back: self.back.reborrow(), + }; + f.debug_list().entries(range).finish() + } +} + +/// An iterator over the keys of a `BTreeMap`. +/// +/// This `struct` is created by the [`keys`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`keys`]: struct.BTreeMap.html#method.keys +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Keys<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, K: 'a + fmt::Debug, V: 'a> fmt::Debug for Keys<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// An iterator over the values of a `BTreeMap`. +/// +/// This `struct` is created by the [`values`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`values`]: struct.BTreeMap.html#method.values +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Values<'a, K: 'a, V: 'a> { + inner: Iter<'a, K, V>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, K: 'a, V: 'a + fmt::Debug> fmt::Debug for Values<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over the values of a `BTreeMap`. +/// +/// This `struct` is created by the [`values_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`values_mut`]: struct.BTreeMap.html#method.values_mut +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "map_values_mut", since = "1.10.0")] +#[derive(Debug)] +pub struct ValuesMut<'a, K: 'a, V: 'a> { + inner: IterMut<'a, K, V>, +} + +/// An iterator over a sub-range of entries in a `BTreeMap`. +/// +/// This `struct` is created by the [`range`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`range`]: struct.BTreeMap.html#method.range +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct Range<'a, K: 'a, V: 'a> { + front: Handle, K, V, marker::Leaf>, marker::Edge>, + back: Handle, K, V, marker::Leaf>, marker::Edge>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for Range<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self.clone()).finish() + } +} + +/// A mutable iterator over a sub-range of entries in a `BTreeMap`. +/// +/// This `struct` is created by the [`range_mut`] method on [`BTreeMap`]. See its +/// documentation for more. +/// +/// [`range_mut`]: struct.BTreeMap.html#method.range_mut +/// [`BTreeMap`]: struct.BTreeMap.html +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct RangeMut<'a, K: 'a, V: 'a> { + front: Handle, K, V, marker::Leaf>, marker::Edge>, + back: Handle, K, V, marker::Leaf>, marker::Edge>, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, K: 'a + fmt::Debug, V: 'a + fmt::Debug> fmt::Debug for RangeMut<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let range = Range { + front: self.front.reborrow(), + back: self.back.reborrow(), + }; + f.debug_list().entries(range).finish() + } +} + +/// A view into a single entry in a map, which may either be vacant or occupied. +/// +/// This `enum` is constructed from the [`entry`] method on [`BTreeMap`]. +/// +/// [`BTreeMap`]: struct.BTreeMap.html +/// [`entry`]: struct.BTreeMap.html#method.entry +#[stable(feature = "rust1", since = "1.0.0")] +pub enum Entry<'a, K: 'a, V: 'a> { + /// A vacant entry. + #[stable(feature = "rust1", since = "1.0.0")] + Vacant(#[stable(feature = "rust1", since = "1.0.0")] + VacantEntry<'a, K, V>), + + /// An occupied entry. + #[stable(feature = "rust1", since = "1.0.0")] + Occupied(#[stable(feature = "rust1", since = "1.0.0")] + OccupiedEntry<'a, K, V>), +} + +#[stable(feature= "debug_btree_map", since = "1.12.0")] +impl<'a, K: 'a + Debug + Ord, V: 'a + Debug> Debug for Entry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + Vacant(ref v) => f.debug_tuple("Entry") + .field(v) + .finish(), + Occupied(ref o) => f.debug_tuple("Entry") + .field(o) + .finish(), + } + } +} + +/// A view into a vacant entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VacantEntry<'a, K: 'a, V: 'a> { + key: K, + handle: Handle, K, V, marker::Leaf>, marker::Edge>, + length: &'a mut usize, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature= "debug_btree_map", since = "1.12.0")] +impl<'a, K: 'a + Debug + Ord, V: 'a> Debug for VacantEntry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("VacantEntry") + .field(self.key()) + .finish() + } +} + +/// A view into an occupied entry in a `BTreeMap`. +/// It is part of the [`Entry`] enum. +/// +/// [`Entry`]: enum.Entry.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct OccupiedEntry<'a, K: 'a, V: 'a> { + handle: Handle, K, V, marker::LeafOrInternal>, marker::KV>, + + length: &'a mut usize, + + // Be invariant in `K` and `V` + _marker: PhantomData<&'a mut (K, V)>, +} + +#[stable(feature= "debug_btree_map", since = "1.12.0")] +impl<'a, K: 'a + Debug + Ord, V: 'a + Debug> Debug for OccupiedEntry<'a, K, V> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("OccupiedEntry") + .field("key", self.key()) + .field("value", self.get()) + .finish() + } +} + +// An iterator for merging two sorted sequences into one +struct MergeIter> { + left: Peekable, + right: Peekable, +} + +impl BTreeMap { + /// Makes a new empty BTreeMap with a reasonable choice for B. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// + /// // entries can now be inserted into the empty map + /// map.insert(1, "a"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> BTreeMap { + BTreeMap { + root: node::Root::shared_empty_root(), + length: 0, + } + } + + /// Clears the map, removing all values. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.clear(); + /// assert!(a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + *self = BTreeMap::new(); + } + + /// Returns a reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get(&1), Some(&"a")); + /// assert_eq!(map.get(&2), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self, key: &Q) -> Option<&V> + where K: Borrow, + Q: Ord + { + match search::search_tree(self.root.as_ref(), key) { + Found(handle) => Some(handle.into_kv().1), + GoDown(_) => None, + } + } + + /// Returns the key-value pair corresponding to the supplied key. + /// + /// The supplied key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// ``` + /// #![feature(map_get_key_value)] + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.get_key_value(&1), Some((&1, &"a"))); + /// assert_eq!(map.get_key_value(&2), None); + /// ``` + #[unstable(feature = "map_get_key_value", issue = "49347")] + pub fn get_key_value(&self, k: &Q) -> Option<(&K, &V)> + where K: Borrow, + Q: Ord + { + match search::search_tree(self.root.as_ref(), k) { + Found(handle) => Some(handle.into_kv()), + GoDown(_) => None, + } + } + + /// Returns `true` if the map contains a value for the specified key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.contains_key(&1), true); + /// assert_eq!(map.contains_key(&2), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains_key(&self, key: &Q) -> bool + where K: Borrow, + Q: Ord + { + self.get(key).is_some() + } + + /// Returns a mutable reference to the value corresponding to the key. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// if let Some(x) = map.get_mut(&1) { + /// *x = "b"; + /// } + /// assert_eq!(map[&1], "b"); + /// ``` + // See `get` for implementation notes, this is basically a copy-paste with mut's added + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self, key: &Q) -> Option<&mut V> + where K: Borrow, + Q: Ord + { + match search::search_tree(self.root.as_mut(), key) { + Found(handle) => Some(handle.into_kv_mut().1), + GoDown(_) => None, + } + } + + /// Inserts a key-value pair into the map. + /// + /// If the map did not have this key present, `None` is returned. + /// + /// If the map did have this key present, the value is updated, and the old + /// value is returned. The key is not updated, though; this matters for + /// types that can be `==` without being identical. See the [module-level + /// documentation] for more. + /// + /// [module-level documentation]: index.html#insert-and-complex-keys + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// assert_eq!(map.insert(37, "a"), None); + /// assert_eq!(map.is_empty(), false); + /// + /// map.insert(37, "b"); + /// assert_eq!(map.insert(37, "c"), Some("b")); + /// assert_eq!(map[&37], "c"); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, key: K, value: V) -> Option { + match self.entry(key) { + Occupied(mut entry) => Some(entry.insert(value)), + Vacant(entry) => { + entry.insert(value); + None + } + } + } + + /// Removes a key from the map, returning the value at the key if the key + /// was previously in the map. + /// + /// The key may be any borrowed form of the map's key type, but the ordering + /// on the borrowed form *must* match the ordering on the key type. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(1, "a"); + /// assert_eq!(map.remove(&1), Some("a")); + /// assert_eq!(map.remove(&1), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, key: &Q) -> Option + where K: Borrow, + Q: Ord + { + match search::search_tree(self.root.as_mut(), key) { + Found(handle) => { + Some(OccupiedEntry { + handle, + length: &mut self.length, + _marker: PhantomData, + } + .remove()) + } + GoDown(_) => None, + } + } + + /// Moves all elements from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.insert(2, "b"); + /// a.insert(3, "c"); + /// + /// let mut b = BTreeMap::new(); + /// b.insert(3, "d"); + /// b.insert(4, "e"); + /// b.insert(5, "f"); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// + /// assert_eq!(a[&1], "a"); + /// assert_eq!(a[&2], "b"); + /// assert_eq!(a[&3], "d"); + /// assert_eq!(a[&4], "e"); + /// assert_eq!(a[&5], "f"); + /// ``` + #[stable(feature = "btree_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + // Do we have to append anything at all? + if other.len() == 0 { + return; + } + + // We can just swap `self` and `other` if `self` is empty. + if self.len() == 0 { + mem::swap(self, other); + return; + } + + // First, we merge `self` and `other` into a sorted sequence in linear time. + let self_iter = mem::replace(self, BTreeMap::new()).into_iter(); + let other_iter = mem::replace(other, BTreeMap::new()).into_iter(); + let iter = MergeIter { + left: self_iter.peekable(), + right: other_iter.peekable(), + }; + + // Second, we build a tree from the sorted sequence in linear time. + self.from_sorted_iter(iter); + self.fix_right_edge(); + } + + /// Constructs a double-ended iterator over a sub-range of elements in the map. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::ops::Bound::Included; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(3, "a"); + /// map.insert(5, "b"); + /// map.insert(8, "c"); + /// for (&key, &value) in map.range((Included(&4), Included(&8))) { + /// println!("{}: {}", key, value); + /// } + /// assert_eq!(Some((&5, &"b")), map.range(4..).next()); + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range(&self, range: R) -> Range + where T: Ord, K: Borrow, R: RangeBounds + { + let root1 = self.root.as_ref(); + let root2 = self.root.as_ref(); + let (f, b) = range_search(root1, root2, range); + + Range { front: f, back: b} + } + + /// Constructs a mutable double-ended iterator over a sub-range of elements in the map. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Panics + /// + /// Panics if range `start > end`. + /// Panics if range `start == end` and both bounds are `Excluded`. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, i32> = ["Alice", "Bob", "Carol", "Cheryl"].iter() + /// .map(|&s| (s, 0)) + /// .collect(); + /// for (_, balance) in map.range_mut("B".."Cheryl") { + /// *balance += 100; + /// } + /// for (name, balance) in &map { + /// println!("{} => {}", name, balance); + /// } + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range_mut(&mut self, range: R) -> RangeMut + where T: Ord, K: Borrow, R: RangeBounds + { + let root1 = self.root.as_mut(); + let root2 = unsafe { ptr::read(&root1) }; + let (f, b) = range_search(root1, root2, range); + + RangeMut { + front: f, + back: b, + _marker: PhantomData, + } + } + + /// Gets the given key's corresponding entry in the map for in-place manipulation. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// // count the number of occurrences of letters in the vec + /// for x in vec!["a","b","a","c","a","b"] { + /// *count.entry(x).or_insert(0) += 1; + /// } + /// + /// assert_eq!(count["a"], 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn entry(&mut self, key: K) -> Entry { + // FIXME(@porglezomp) Avoid allocating if we don't insert + self.ensure_root_is_owned(); + match search::search_tree(self.root.as_mut(), &key) { + Found(handle) => { + Occupied(OccupiedEntry { + handle, + length: &mut self.length, + _marker: PhantomData, + }) + } + GoDown(handle) => { + Vacant(VacantEntry { + key, + handle, + length: &mut self.length, + _marker: PhantomData, + }) + } + } + } + + fn from_sorted_iter>(&mut self, iter: I) { + self.ensure_root_is_owned(); + let mut cur_node = last_leaf_edge(self.root.as_mut()).into_node(); + // Iterate through all key-value pairs, pushing them into nodes at the right level. + for (key, value) in iter { + // Try to push key-value pair into the current leaf node. + if cur_node.len() < node::CAPACITY { + cur_node.push(key, value); + } else { + // No space left, go up and push there. + let mut open_node; + let mut test_node = cur_node.forget_type(); + loop { + match test_node.ascend() { + Ok(parent) => { + let parent = parent.into_node(); + if parent.len() < node::CAPACITY { + // Found a node with space left, push here. + open_node = parent; + break; + } else { + // Go up again. + test_node = parent.forget_type(); + } + } + Err(node) => { + // We are at the top, create a new root node and push there. + open_node = node.into_root_mut().push_level(); + break; + } + } + } + + // Push key-value pair and new right subtree. + let tree_height = open_node.height() - 1; + let mut right_tree = node::Root::new_leaf(); + for _ in 0..tree_height { + right_tree.push_level(); + } + open_node.push(key, value, right_tree); + + // Go down to the right-most leaf again. + cur_node = last_leaf_edge(open_node.forget_type()).into_node(); + } + + self.length += 1; + } + } + + fn fix_right_edge(&mut self) { + // Handle underfull nodes, start from the top. + let mut cur_node = self.root.as_mut(); + while let Internal(internal) = cur_node.force() { + // Check if right-most child is underfull. + let mut last_edge = internal.last_edge(); + let right_child_len = last_edge.reborrow().descend().len(); + if right_child_len < node::MIN_LEN { + // We need to steal. + let mut last_kv = match last_edge.left_kv() { + Ok(left) => left, + Err(_) => unreachable!(), + }; + last_kv.bulk_steal_left(node::MIN_LEN - right_child_len); + last_edge = last_kv.right_edge(); + } + + // Go further down. + cur_node = last_edge.descend(); + } + } + + /// Splits the collection into two at the given key. Returns everything after the given key, + /// including the key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "a"); + /// a.insert(2, "b"); + /// a.insert(3, "c"); + /// a.insert(17, "d"); + /// a.insert(41, "e"); + /// + /// let b = a.split_off(&3); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 3); + /// + /// assert_eq!(a[&1], "a"); + /// assert_eq!(a[&2], "b"); + /// + /// assert_eq!(b[&3], "c"); + /// assert_eq!(b[&17], "d"); + /// assert_eq!(b[&41], "e"); + /// ``` + #[stable(feature = "btree_split_off", since = "1.11.0")] + pub fn split_off(&mut self, key: &Q) -> Self + where K: Borrow + { + if self.is_empty() { + return Self::new(); + } + + let total_num = self.len(); + + let mut right = Self::new(); + right.root = node::Root::new_leaf(); + for _ in 0..(self.root.as_ref().height()) { + right.root.push_level(); + } + + { + let mut left_node = self.root.as_mut(); + let mut right_node = right.root.as_mut(); + + loop { + let mut split_edge = match search::search_node(left_node, key) { + // key is going to the right tree + Found(handle) => handle.left_edge(), + GoDown(handle) => handle, + }; + + split_edge.move_suffix(&mut right_node); + + match (split_edge.force(), right_node.force()) { + (Internal(edge), Internal(node)) => { + left_node = edge.descend(); + right_node = node.first_edge().descend(); + } + (Leaf(_), Leaf(_)) => { + break; + } + _ => { + unreachable!(); + } + } + } + } + + self.fix_right_border(); + right.fix_left_border(); + + if self.root.as_ref().height() < right.root.as_ref().height() { + self.recalc_length(); + right.length = total_num - self.len(); + } else { + right.recalc_length(); + self.length = total_num - right.len(); + } + + right + } + + /// Calculates the number of elements if it is incorrect. + fn recalc_length(&mut self) { + fn dfs(node: NodeRef) -> usize { + let mut res = node.len(); + + if let Internal(node) = node.force() { + let mut edge = node.first_edge(); + loop { + res += dfs(edge.reborrow().descend()); + match edge.right_kv() { + Ok(right_kv) => { + edge = right_kv.right_edge(); + } + Err(_) => { + break; + } + } + } + } + + res + } + + self.length = dfs(self.root.as_ref()); + } + + /// Removes empty levels on the top. + fn fix_top(&mut self) { + loop { + { + let node = self.root.as_ref(); + if node.height() == 0 || node.len() > 0 { + break; + } + } + self.root.pop_level(); + } + } + + fn fix_right_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.root.as_mut(); + + while let Internal(node) = cur_node.force() { + let mut last_kv = node.last_kv(); + + if last_kv.can_merge() { + cur_node = last_kv.merge().descend(); + } else { + let right_len = last_kv.reborrow().right_edge().descend().len(); + // `MINLEN + 1` to avoid readjust if merge happens on the next level. + if right_len < node::MIN_LEN + 1 { + last_kv.bulk_steal_left(node::MIN_LEN + 1 - right_len); + } + cur_node = last_kv.right_edge().descend(); + } + } + } + + self.fix_top(); + } + + /// The symmetric clone of `fix_right_border`. + fn fix_left_border(&mut self) { + self.fix_top(); + + { + let mut cur_node = self.root.as_mut(); + + while let Internal(node) = cur_node.force() { + let mut first_kv = node.first_kv(); + + if first_kv.can_merge() { + cur_node = first_kv.merge().descend(); + } else { + let left_len = first_kv.reborrow().left_edge().descend().len(); + if left_len < node::MIN_LEN + 1 { + first_kv.bulk_steal_right(node::MIN_LEN + 1 - left_len); + } + cur_node = first_kv.left_edge().descend(); + } + } + } + + self.fix_top(); + } + + /// If the root node is the shared root node, allocate our own node. + fn ensure_root_is_owned(&mut self) { + if self.root.is_shared_root() { + self.root = node::Root::new_leaf(); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> IntoIterator for &'a BTreeMap { + type Item = (&'a K, &'a V); + type IntoIter = Iter<'a, K, V>; + + fn into_iter(self) -> Iter<'a, K, V> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> Iterator for Iter<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option<(&'a K, &'a V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_unchecked()) } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for Iter<'a, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> DoubleEndedIterator for Iter<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_back_unchecked()) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> ExactSizeIterator for Iter<'a, K, V> { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Clone for Iter<'a, K, V> { + fn clone(&self) -> Iter<'a, K, V> { + Iter { + range: self.range.clone(), + length: self.length, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> IntoIterator for &'a mut BTreeMap { + type Item = (&'a K, &'a mut V); + type IntoIter = IterMut<'a, K, V>; + + fn into_iter(self) -> IterMut<'a, K, V> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> Iterator for IterMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_unchecked()) } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> DoubleEndedIterator for IterMut<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.length == 0 { + None + } else { + self.length -= 1; + unsafe { Some(self.range.next_back_unchecked()) } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: 'a, V: 'a> ExactSizeIterator for IterMut<'a, K, V> { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for IterMut<'a, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BTreeMap { + type Item = (K, V); + type IntoIter = IntoIter; + + fn into_iter(self) -> IntoIter { + let root1 = unsafe { ptr::read(&self.root).into_ref() }; + let root2 = unsafe { ptr::read(&self.root).into_ref() }; + let len = self.length; + mem::forget(self); + + IntoIter { + front: first_leaf_edge(root1), + back: last_leaf_edge(root2), + length: len, + } + } +} + +#[stable(feature = "btree_drop", since = "1.7.0")] +impl Drop for IntoIter { + fn drop(&mut self) { + self.for_each(drop); + unsafe { + let leaf_node = ptr::read(&self.front).into_node(); + if leaf_node.is_shared_root() { + return; + } + + if let Some(first_parent) = leaf_node.deallocate_and_ascend() { + let mut cur_node = first_parent.into_node(); + while let Some(parent) = cur_node.deallocate_and_ascend() { + cur_node = parent.into_node() + } + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + if self.length == 0 { + return None; + } else { + self.length -= 1; + } + + let handle = unsafe { ptr::read(&self.front) }; + + let mut cur_handle = match handle.right_kv() { + Ok(kv) => { + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + self.front = kv.right_edge(); + return Some((k, v)); + } + Err(last_edge) => unsafe { + unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()) + }, + }; + + loop { + match cur_handle.right_kv() { + Ok(kv) => { + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + self.front = first_leaf_edge(kv.right_edge().descend()); + return Some((k, v)); + } + Err(last_edge) => unsafe { + cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()); + }, + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (self.length, Some(self.length)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option<(K, V)> { + if self.length == 0 { + return None; + } else { + self.length -= 1; + } + + let handle = unsafe { ptr::read(&self.back) }; + + let mut cur_handle = match handle.left_kv() { + Ok(kv) => { + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + self.back = kv.left_edge(); + return Some((k, v)); + } + Err(last_edge) => unsafe { + unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()) + }, + }; + + loop { + match cur_handle.left_kv() { + Ok(kv) => { + let k = unsafe { ptr::read(kv.reborrow().into_kv().0) }; + let v = unsafe { ptr::read(kv.reborrow().into_kv().1) }; + self.back = last_leaf_edge(kv.left_edge().descend()); + return Some((k, v)); + } + Err(last_edge) => unsafe { + cur_handle = unwrap_unchecked(last_edge.into_node().deallocate_and_ascend()); + }, + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { + self.length + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Keys<'a, K, V> { + type Item = &'a K; + + fn next(&mut self) -> Option<&'a K> { + self.inner.next().map(|(k, _)| k) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> DoubleEndedIterator for Keys<'a, K, V> { + fn next_back(&mut self) -> Option<&'a K> { + self.inner.next_back().map(|(k, _)| k) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> ExactSizeIterator for Keys<'a, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for Keys<'a, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Clone for Keys<'a, K, V> { + fn clone(&self) -> Keys<'a, K, V> { + Keys { inner: self.inner.clone() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Iterator for Values<'a, K, V> { + type Item = &'a V; + + fn next(&mut self) -> Option<&'a V> { + self.inner.next().map(|(_, v)| v) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> DoubleEndedIterator for Values<'a, K, V> { + fn next_back(&mut self) -> Option<&'a V> { + self.inner.next_back().map(|(_, v)| v) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> ExactSizeIterator for Values<'a, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for Values<'a, K, V> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K, V> Clone for Values<'a, K, V> { + fn clone(&self) -> Values<'a, K, V> { + Values { inner: self.inner.clone() } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> Iterator for Range<'a, K, V> { + type Item = (&'a K, &'a V); + + fn next(&mut self) -> Option<(&'a K, &'a V)> { + if self.front == self.back { + None + } else { + unsafe { Some(self.next_unchecked()) } + } + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> Iterator for ValuesMut<'a, K, V> { + type Item = &'a mut V; + + fn next(&mut self) -> Option<&'a mut V> { + self.inner.next().map(|(_, v)| v) + } + + fn size_hint(&self) -> (usize, Option) { + self.inner.size_hint() + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> DoubleEndedIterator for ValuesMut<'a, K, V> { + fn next_back(&mut self) -> Option<&'a mut V> { + self.inner.next_back().map(|(_, v)| v) + } +} + +#[stable(feature = "map_values_mut", since = "1.10.0")] +impl<'a, K, V> ExactSizeIterator for ValuesMut<'a, K, V> { + fn len(&self) -> usize { + self.inner.len() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for ValuesMut<'a, K, V> {} + + +impl<'a, K, V> Range<'a, K, V> { + unsafe fn next_unchecked(&mut self) -> (&'a K, &'a V) { + let handle = self.front; + + let mut cur_handle = match handle.right_kv() { + Ok(kv) => { + let ret = kv.into_kv(); + self.front = kv.right_edge(); + return ret; + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + unwrap_unchecked(next_level) + } + }; + + loop { + match cur_handle.right_kv() { + Ok(kv) => { + let ret = kv.into_kv(); + self.front = first_leaf_edge(kv.right_edge().descend()); + return ret; + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + cur_handle = unwrap_unchecked(next_level); + } + } + } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> DoubleEndedIterator for Range<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a V)> { + if self.front == self.back { + None + } else { + unsafe { Some(self.next_back_unchecked()) } + } + } +} + +impl<'a, K, V> Range<'a, K, V> { + unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a V) { + let handle = self.back; + + let mut cur_handle = match handle.left_kv() { + Ok(kv) => { + let ret = kv.into_kv(); + self.back = kv.left_edge(); + return ret; + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + unwrap_unchecked(next_level) + } + }; + + loop { + match cur_handle.left_kv() { + Ok(kv) => { + let ret = kv.into_kv(); + self.back = last_leaf_edge(kv.left_edge().descend()); + return ret; + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + cur_handle = unwrap_unchecked(next_level); + } + } + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for Range<'a, K, V> {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> Clone for Range<'a, K, V> { + fn clone(&self) -> Range<'a, K, V> { + Range { + front: self.front, + back: self.back, + } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> Iterator for RangeMut<'a, K, V> { + type Item = (&'a K, &'a mut V); + + fn next(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.front == self.back { + None + } else { + unsafe { Some(self.next_unchecked()) } + } + } +} + +impl<'a, K, V> RangeMut<'a, K, V> { + unsafe fn next_unchecked(&mut self) -> (&'a K, &'a mut V) { + let handle = ptr::read(&self.front); + + let mut cur_handle = match handle.right_kv() { + Ok(kv) => { + let (k, v) = ptr::read(&kv).into_kv_mut(); + self.front = kv.right_edge(); + return (k, v); + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + unwrap_unchecked(next_level) + } + }; + + loop { + match cur_handle.right_kv() { + Ok(kv) => { + let (k, v) = ptr::read(&kv).into_kv_mut(); + self.front = first_leaf_edge(kv.right_edge().descend()); + return (k, v); + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + cur_handle = unwrap_unchecked(next_level); + } + } + } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, K, V> DoubleEndedIterator for RangeMut<'a, K, V> { + fn next_back(&mut self) -> Option<(&'a K, &'a mut V)> { + if self.front == self.back { + None + } else { + unsafe { Some(self.next_back_unchecked()) } + } + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, K, V> FusedIterator for RangeMut<'a, K, V> {} + +impl<'a, K, V> RangeMut<'a, K, V> { + unsafe fn next_back_unchecked(&mut self) -> (&'a K, &'a mut V) { + let handle = ptr::read(&self.back); + + let mut cur_handle = match handle.left_kv() { + Ok(kv) => { + let (k, v) = ptr::read(&kv).into_kv_mut(); + self.back = kv.left_edge(); + return (k, v); + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + unwrap_unchecked(next_level) + } + }; + + loop { + match cur_handle.left_kv() { + Ok(kv) => { + let (k, v) = ptr::read(&kv).into_kv_mut(); + self.back = last_leaf_edge(kv.left_edge().descend()); + return (k, v); + } + Err(last_edge) => { + let next_level = last_edge.into_node().ascend().ok(); + cur_handle = unwrap_unchecked(next_level); + } + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator<(K, V)> for BTreeMap { + fn from_iter>(iter: T) -> BTreeMap { + let mut map = BTreeMap::new(); + map.extend(iter); + map + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend<(K, V)> for BTreeMap { + #[inline] + fn extend>(&mut self, iter: T) { + for (k, v) in iter { + self.insert(k, v); + } + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, K: Ord + Copy, V: Copy> Extend<(&'a K, &'a V)> for BTreeMap { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().map(|(&key, &value)| (key, value))); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for BTreeMap { + fn hash(&self, state: &mut H) { + for elt in self { + elt.hash(state); + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BTreeMap { + /// Creates an empty `BTreeMap`. + fn default() -> BTreeMap { + BTreeMap::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for BTreeMap { + fn eq(&self, other: &BTreeMap) -> bool { + self.len() == other.len() && self.iter().zip(other).all(|(a, b)| a == b) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for BTreeMap {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for BTreeMap { + #[inline] + fn partial_cmp(&self, other: &BTreeMap) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for BTreeMap { + #[inline] + fn cmp(&self, other: &BTreeMap) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for BTreeMap { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_map().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, K: Ord, Q: ?Sized, V> Index<&'a Q> for BTreeMap + where K: Borrow, + Q: Ord +{ + type Output = V; + + /// Returns a reference to the value corresponding to the supplied key. + /// + /// # Panics + /// + /// Panics if the key is not present in the `BTreeMap`. + #[inline] + fn index(&self, key: &Q) -> &V { + self.get(key).expect("no entry found for key") + } +} + +fn first_leaf_edge + (mut node: NodeRef) + -> Handle, marker::Edge> { + loop { + match node.force() { + Leaf(leaf) => return leaf.first_edge(), + Internal(internal) => { + node = internal.first_edge().descend(); + } + } + } +} + +fn last_leaf_edge + (mut node: NodeRef) + -> Handle, marker::Edge> { + loop { + match node.force() { + Leaf(leaf) => return leaf.last_edge(), + Internal(internal) => { + node = internal.last_edge().descend(); + } + } + } +} + +fn range_search>( + root1: NodeRef, + root2: NodeRef, + range: R +)-> (Handle, marker::Edge>, + Handle, marker::Edge>) + where Q: Ord, K: Borrow +{ + match (range.start_bound(), range.end_bound()) { + (Excluded(s), Excluded(e)) if s==e => + panic!("range start and end are equal and excluded in BTreeMap"), + (Included(s), Included(e)) | + (Included(s), Excluded(e)) | + (Excluded(s), Included(e)) | + (Excluded(s), Excluded(e)) if s>e => + panic!("range start is greater than range end in BTreeMap"), + _ => {}, + }; + + let mut min_node = root1; + let mut max_node = root2; + let mut min_found = false; + let mut max_found = false; + let mut diverged = false; + + loop { + let min_edge = match (min_found, range.start_bound()) { + (false, Included(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&min_node, key) { + (i, true) => { min_found = true; i+1 }, + (i, false) => i, + }, + (_, Unbounded) => 0, + (true, Included(_)) => min_node.keys().len(), + (true, Excluded(_)) => 0, + }; + + let max_edge = match (max_found, range.end_bound()) { + (false, Included(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i+1 }, + (i, false) => i, + }, + (false, Excluded(key)) => match search::search_linear(&max_node, key) { + (i, true) => { max_found = true; i }, + (i, false) => i, + }, + (_, Unbounded) => max_node.keys().len(), + (true, Included(_)) => 0, + (true, Excluded(_)) => max_node.keys().len(), + }; + + if !diverged { + if max_edge < min_edge { panic!("Ord is ill-defined in BTreeMap range") } + if min_edge != max_edge { diverged = true; } + } + + let front = Handle::new_edge(min_node, min_edge); + let back = Handle::new_edge(max_node, max_edge); + match (front.force(), back.force()) { + (Leaf(f), Leaf(b)) => { + return (f, b); + }, + (Internal(min_int), Internal(max_int)) => { + min_node = min_int.descend(); + max_node = max_int.descend(); + }, + _ => unreachable!("BTreeMap has different depths"), + }; + } +} + +#[inline(always)] +unsafe fn unwrap_unchecked(val: Option) -> T { + val.unwrap_or_else(|| { + if cfg!(debug_assertions) { + panic!("'unchecked' unwrap on None in BTreeMap"); + } else { + intrinsics::unreachable(); + } + }) +} + +impl BTreeMap { + /// Gets an iterator over the entries of the map, sorted by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert(3, "c"); + /// map.insert(2, "b"); + /// map.insert(1, "a"); + /// + /// for (key, value) in map.iter() { + /// println!("{}: {}", key, value); + /// } + /// + /// let (first_key, first_value) = map.iter().next().unwrap(); + /// assert_eq!((*first_key, *first_value), (1, "a")); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { + range: Range { + front: first_leaf_edge(self.root.as_ref()), + back: last_leaf_edge(self.root.as_ref()), + }, + length: self.length, + } + } + + /// Gets a mutable iterator over the entries of the map, sorted by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map = BTreeMap::new(); + /// map.insert("a", 1); + /// map.insert("b", 2); + /// map.insert("c", 3); + /// + /// // add 10 to the value if the key isn't "a" + /// for (key, value) in map.iter_mut() { + /// if key != &"a" { + /// *value += 10; + /// } + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut { + let root1 = self.root.as_mut(); + let root2 = unsafe { ptr::read(&root1) }; + IterMut { + range: RangeMut { + front: first_leaf_edge(root1), + back: last_leaf_edge(root2), + _marker: PhantomData, + }, + length: self.length, + } + } + + /// Gets an iterator over the keys of the map, in sorted order. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(2, "b"); + /// a.insert(1, "a"); + /// + /// let keys: Vec<_> = a.keys().cloned().collect(); + /// assert_eq!(keys, [1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn keys<'a>(&'a self) -> Keys<'a, K, V> { + Keys { inner: self.iter() } + } + + /// Gets an iterator over the values of the map, in order by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, "hello"); + /// a.insert(2, "goodbye"); + /// + /// let values: Vec<&str> = a.values().cloned().collect(); + /// assert_eq!(values, ["hello", "goodbye"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn values<'a>(&'a self) -> Values<'a, K, V> { + Values { inner: self.iter() } + } + + /// Gets a mutable iterator over the values of the map, in order by key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// a.insert(1, String::from("hello")); + /// a.insert(2, String::from("goodbye")); + /// + /// for value in a.values_mut() { + /// value.push_str("!"); + /// } + /// + /// let values: Vec = a.values().cloned().collect(); + /// assert_eq!(values, [String::from("hello!"), + /// String::from("goodbye!")]); + /// ``` + #[stable(feature = "map_values_mut", since = "1.10.0")] + pub fn values_mut(&mut self) -> ValuesMut { + ValuesMut { inner: self.iter_mut() } + } + + /// Returns the number of elements in the map. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// assert_eq!(a.len(), 0); + /// a.insert(1, "a"); + /// assert_eq!(a.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.length + } + + /// Returns `true` if the map contains no elements. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut a = BTreeMap::new(); + /// assert!(a.is_empty()); + /// a.insert(1, "a"); + /// assert!(!a.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +impl<'a, K: Ord, V> Entry<'a, K, V> { + /// Ensures a value is in the entry by inserting the default if empty, and returns + /// a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert(self, default: V) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default), + } + } + + /// Ensures a value is in the entry by inserting the result of the default function if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, String> = BTreeMap::new(); + /// let s = "hoho".to_string(); + /// + /// map.entry("poneyland").or_insert_with(|| s); + /// + /// assert_eq!(map["poneyland"], "hoho".to_string()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn or_insert_with V>(self, default: F) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(default()), + } + } + + /// Returns a reference to this entry's key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + match *self { + Occupied(ref entry) => entry.key(), + Vacant(ref entry) => entry.key(), + } + } + + /// Provides in-place mutable access to an occupied entry before any + /// potential inserts into the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 42); + /// + /// map.entry("poneyland") + /// .and_modify(|e| { *e += 1 }) + /// .or_insert(42); + /// assert_eq!(map["poneyland"], 43); + /// ``` + #[stable(feature = "entry_and_modify", since = "1.26.0")] + pub fn and_modify(self, f: F) -> Self + where F: FnOnce(&mut V) + { + match self { + Occupied(mut entry) => { + f(entry.get_mut()); + Occupied(entry) + }, + Vacant(entry) => Vacant(entry), + } + } +} + +impl<'a, K: Ord, V: Default> Entry<'a, K, V> { + #[stable(feature = "entry_or_default", since = "1.28.0")] + /// Ensures a value is in the entry by inserting the default value if empty, + /// and returns a mutable reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// # fn main() { + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, Option> = BTreeMap::new(); + /// map.entry("poneyland").or_default(); + /// + /// assert_eq!(map["poneyland"], None); + /// # } + /// ``` + pub fn or_default(self) -> &'a mut V { + match self { + Occupied(entry) => entry.into_mut(), + Vacant(entry) => entry.insert(Default::default()), + } + } + +} + +impl<'a, K: Ord, V> VacantEntry<'a, K, V> { + /// Gets a reference to the key that would be used when inserting a value + /// through the VacantEntry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + &self.key + } + + /// Take ownership of the key. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// if let Entry::Vacant(v) = map.entry("poneyland") { + /// v.into_key(); + /// } + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn into_key(self) -> K { + self.key + } + + /// Sets the value of the entry with the `VacantEntry`'s key, + /// and returns a mutable reference to it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut count: BTreeMap<&str, usize> = BTreeMap::new(); + /// + /// // count the number of occurrences of letters in the vec + /// for x in vec!["a","b","a","c","a","b"] { + /// *count.entry(x).or_insert(0) += 1; + /// } + /// + /// assert_eq!(count["a"], 3); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(self, value: V) -> &'a mut V { + *self.length += 1; + + let out_ptr; + + let mut ins_k; + let mut ins_v; + let mut ins_edge; + + let mut cur_parent = match self.handle.insert(self.key, value) { + (Fit(handle), _) => return handle.into_kv_mut().1, + (Split(left, k, v, right), ptr) => { + ins_k = k; + ins_v = v; + ins_edge = right; + out_ptr = ptr; + left.ascend().map_err(|n| n.into_root_mut()) + } + }; + + loop { + match cur_parent { + Ok(parent) => { + match parent.insert(ins_k, ins_v, ins_edge) { + Fit(_) => return unsafe { &mut *out_ptr }, + Split(left, k, v, right) => { + ins_k = k; + ins_v = v; + ins_edge = right; + cur_parent = left.ascend().map_err(|n| n.into_root_mut()); + } + } + } + Err(root) => { + root.push_level().push(ins_k, ins_v, ins_edge); + return unsafe { &mut *out_ptr }; + } + } + } + } +} + +impl<'a, K: Ord, V> OccupiedEntry<'a, K, V> { + /// Gets a reference to the key in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// assert_eq!(map.entry("poneyland").key(), &"poneyland"); + /// ``` + #[stable(feature = "map_entry_keys", since = "1.10.0")] + pub fn key(&self) -> &K { + self.handle.reborrow().into_kv().0 + } + + /// Take ownership of the key and value from the map. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// // We delete the entry from the map. + /// o.remove_entry(); + /// } + /// + /// // If now try to get the value, it will panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "map_entry_recover_keys2", since = "1.12.0")] + pub fn remove_entry(self) -> (K, V) { + self.remove_kv() + } + + /// Gets a reference to the value in the entry. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.get(), &12); + /// } + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self) -> &V { + self.handle.reborrow().into_kv().1 + } + + /// Gets a mutable reference to the value in the entry. + /// + /// If you need a reference to the `OccupiedEntry` which may outlive the + /// destruction of the `Entry` value, see [`into_mut`]. + /// + /// [`into_mut`]: #method.into_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// *o.get_mut() += 10; + /// assert_eq!(*o.get(), 22); + /// + /// // We can use the same Entry multiple times. + /// *o.get_mut() += 2; + /// } + /// assert_eq!(map["poneyland"], 24); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self) -> &mut V { + self.handle.kv_mut().1 + } + + /// Converts the entry into a mutable reference to its value. + /// + /// If you need multiple references to the `OccupiedEntry`, see [`get_mut`]. + /// + /// [`get_mut`]: #method.get_mut + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// assert_eq!(map["poneyland"], 12); + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// *o.into_mut() += 10; + /// } + /// assert_eq!(map["poneyland"], 22); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn into_mut(self) -> &'a mut V { + self.handle.into_kv_mut().1 + } + + /// Sets the value of the entry with the `OccupiedEntry`'s key, + /// and returns the entry's old value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(mut o) = map.entry("poneyland") { + /// assert_eq!(o.insert(15), 12); + /// } + /// assert_eq!(map["poneyland"], 15); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: V) -> V { + mem::replace(self.get_mut(), value) + } + + /// Takes the value of the entry out of the map, and returns it. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeMap; + /// use std::collections::btree_map::Entry; + /// + /// let mut map: BTreeMap<&str, usize> = BTreeMap::new(); + /// map.entry("poneyland").or_insert(12); + /// + /// if let Entry::Occupied(o) = map.entry("poneyland") { + /// assert_eq!(o.remove(), 12); + /// } + /// // If we try to get "poneyland"'s value, it'll panic: + /// // println!("{}", map["poneyland"]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(self) -> V { + self.remove_kv().1 + } + + fn remove_kv(self) -> (K, V) { + *self.length -= 1; + + let (small_leaf, old_key, old_val) = match self.handle.force() { + Leaf(leaf) => { + let (hole, old_key, old_val) = leaf.remove(); + (hole.into_node(), old_key, old_val) + } + Internal(mut internal) => { + let key_loc = internal.kv_mut().0 as *mut K; + let val_loc = internal.kv_mut().1 as *mut V; + + let to_remove = first_leaf_edge(internal.right_edge().descend()).right_kv().ok(); + let to_remove = unsafe { unwrap_unchecked(to_remove) }; + + let (hole, key, val) = to_remove.remove(); + + let old_key = unsafe { mem::replace(&mut *key_loc, key) }; + let old_val = unsafe { mem::replace(&mut *val_loc, val) }; + + (hole.into_node(), old_key, old_val) + } + }; + + // Handle underflow + let mut cur_node = small_leaf.forget_type(); + while cur_node.len() < node::CAPACITY / 2 { + match handle_underfull_node(cur_node) { + AtRoot => break, + EmptyParent(_) => unreachable!(), + Merged(parent) => { + if parent.len() == 0 { + // We must be at the root + parent.into_root_mut().pop_level(); + break; + } else { + cur_node = parent.forget_type(); + } + } + Stole(_) => break, + } + } + + (old_key, old_val) + } +} + +enum UnderflowResult<'a, K, V> { + AtRoot, + EmptyParent(NodeRef, K, V, marker::Internal>), + Merged(NodeRef, K, V, marker::Internal>), + Stole(NodeRef, K, V, marker::Internal>), +} + +fn handle_underfull_node<'a, K, V>(node: NodeRef, K, V, marker::LeafOrInternal>) + -> UnderflowResult<'a, K, V> { + let parent = if let Ok(parent) = node.ascend() { + parent + } else { + return AtRoot; + }; + + let (is_left, mut handle) = match parent.left_kv() { + Ok(left) => (true, left), + Err(parent) => { + match parent.right_kv() { + Ok(right) => (false, right), + Err(parent) => { + return EmptyParent(parent.into_node()); + } + } + } + }; + + if handle.can_merge() { + Merged(handle.merge().into_node()) + } else { + if is_left { + handle.steal_left(); + } else { + handle.steal_right(); + } + Stole(handle.into_node()) + } +} + +impl> Iterator for MergeIter { + type Item = (K, V); + + fn next(&mut self) -> Option<(K, V)> { + let res = match (self.left.peek(), self.right.peek()) { + (Some(&(ref left_key, _)), Some(&(ref right_key, _))) => left_key.cmp(right_key), + (Some(_), None) => Ordering::Less, + (None, Some(_)) => Ordering::Greater, + (None, None) => return None, + }; + + // Check which elements comes first and only advance the corresponding iterator. + // If two keys are equal, take the value from `right`. + match res { + Ordering::Less => self.left.next(), + Ordering::Greater => self.right.next(), + Ordering::Equal => { + self.left.next(); + self.right.next() + } + } + } +} diff --git a/src/liballoc/collections/btree/mod.rs b/src/liballoc/collections/btree/mod.rs new file mode 100644 index 00000000000..087c9f228d4 --- /dev/null +++ b/src/liballoc/collections/btree/mod.rs @@ -0,0 +1,23 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +mod node; +mod search; +pub mod map; +pub mod set; + +#[doc(hidden)] +trait Recover { + type Key; + + fn get(&self, key: &Q) -> Option<&Self::Key>; + fn take(&mut self, key: &Q) -> Option; + fn replace(&mut self, key: Self::Key) -> Option; +} diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs new file mode 100644 index 00000000000..19bdcbc6ad6 --- /dev/null +++ b/src/liballoc/collections/btree/node.rs @@ -0,0 +1,1622 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is an attempt at an implementation following the ideal +// +// ``` +// struct BTreeMap { +// height: usize, +// root: Option>> +// } +// +// struct Node { +// keys: [K; 2 * B - 1], +// vals: [V; 2 * B - 1], +// edges: if height > 0 { +// [Box>; 2 * B] +// } else { () }, +// parent: *const Node, +// parent_idx: u16, +// len: u16, +// } +// ``` +// +// Since Rust doesn't actually have dependent types and polymorphic recursion, +// we make do with lots of unsafety. + +// A major goal of this module is to avoid complexity by treating the tree as a generic (if +// weirdly shaped) container and avoiding dealing with most of the B-Tree invariants. As such, +// this module doesn't care whether the entries are sorted, which nodes can be underfull, or +// even what underfull means. However, we do rely on a few invariants: +// +// - Trees must have uniform depth/height. This means that every path down to a leaf from a +// given node has exactly the same length. +// - A node of length `n` has `n` keys, `n` values, and (in an internal node) `n + 1` edges. +// This implies that even an empty internal node has at least one edge. + +use core::marker::PhantomData; +use core::mem; +use core::ptr::{self, Unique, NonNull}; +use core::slice; + +use alloc::{Global, Alloc, Layout}; +use boxed::Box; + +const B: usize = 6; +pub const MIN_LEN: usize = B - 1; +pub const CAPACITY: usize = 2 * B - 1; + +/// The underlying representation of leaf nodes. Note that it is often unsafe to actually store +/// these, since only the first `len` keys and values are assumed to be initialized. As such, +/// these should always be put behind pointers, and specifically behind `BoxedNode` in the owned +/// case. +/// +/// See also rust-lang/rfcs#197, which would make this structure significantly more safe by +/// avoiding accidentally dropping unused and uninitialized keys and values. +/// +/// We put the metadata first so that its position is the same for every `K` and `V`, in order +/// to statically allocate a single dummy node to avoid allocations. This struct is `repr(C)` to +/// prevent them from being reordered. +#[repr(C)] +struct LeafNode { + /// We use `*const` as opposed to `*mut` so as to be covariant in `K` and `V`. + /// This either points to an actual node or is null. + parent: *const InternalNode, + + /// This node's index into the parent node's `edges` array. + /// `*node.parent.edges[node.parent_idx]` should be the same thing as `node`. + /// This is only guaranteed to be initialized when `parent` is nonnull. + parent_idx: u16, + + /// The number of keys and values this node stores. + /// + /// This next to `parent_idx` to encourage the compiler to join `len` and + /// `parent_idx` into the same 32-bit word, reducing space overhead. + len: u16, + + /// The arrays storing the actual data of the node. Only the first `len` elements of each + /// array are initialized and valid. + keys: [K; CAPACITY], + vals: [V; CAPACITY], +} + +impl LeafNode { + /// Creates a new `LeafNode`. Unsafe because all nodes should really be hidden behind + /// `BoxedNode`, preventing accidental dropping of uninitialized keys and values. + unsafe fn new() -> Self { + LeafNode { + // As a general policy, we leave fields uninitialized if they can be, as this should + // be both slightly faster and easier to track in Valgrind. + keys: mem::uninitialized(), + vals: mem::uninitialized(), + parent: ptr::null(), + parent_idx: mem::uninitialized(), + len: 0 + } + } + + fn is_shared_root(&self) -> bool { + self as *const _ == &EMPTY_ROOT_NODE as *const _ as *const LeafNode + } +} + +// We need to implement Sync here in order to make a static instance. +unsafe impl Sync for LeafNode<(), ()> {} + +// An empty node used as a placeholder for the root node, to avoid allocations. +// We use () in order to save space, since no operation on an empty tree will +// ever take a pointer past the first key. +static EMPTY_ROOT_NODE: LeafNode<(), ()> = LeafNode { + parent: ptr::null(), + parent_idx: 0, + len: 0, + keys: [(); CAPACITY], + vals: [(); CAPACITY], +}; + +/// The underlying representation of internal nodes. As with `LeafNode`s, these should be hidden +/// behind `BoxedNode`s to prevent dropping uninitialized keys and values. Any pointer to an +/// `InternalNode` can be directly casted to a pointer to the underlying `LeafNode` portion of the +/// node, allowing code to act on leaf and internal nodes generically without having to even check +/// which of the two a pointer is pointing at. This property is enabled by the use of `repr(C)`. +#[repr(C)] +struct InternalNode { + data: LeafNode, + + /// The pointers to the children of this node. `len + 1` of these are considered + /// initialized and valid. + edges: [BoxedNode; 2 * B], +} + +impl InternalNode { + /// Creates a new `InternalNode`. + /// + /// This is unsafe for two reasons. First, it returns an `InternalNode` by value, risking + /// dropping of uninitialized fields. Second, an invariant of internal nodes is that `len + 1` + /// edges are initialized and valid, meaning that even when the node is empty (having a + /// `len` of 0), there must be one initialized and valid edge. This function does not set up + /// such an edge. + unsafe fn new() -> Self { + InternalNode { + data: LeafNode::new(), + edges: mem::uninitialized() + } + } +} + +/// An owned pointer to a node. This basically is either `Box>` or +/// `Box>`. However, it contains no information as to which of the two types +/// of nodes is actually behind the box, and, partially due to this lack of information, has no +/// destructor. +struct BoxedNode { + ptr: Unique> +} + +impl BoxedNode { + fn from_leaf(node: Box>) -> Self { + BoxedNode { ptr: Box::into_unique(node) } + } + + fn from_internal(node: Box>) -> Self { + unsafe { + BoxedNode { ptr: Unique::new_unchecked(Box::into_raw(node) as *mut LeafNode) } + } + } + + unsafe fn from_ptr(ptr: NonNull>) -> Self { + BoxedNode { ptr: Unique::from(ptr) } + } + + fn as_ptr(&self) -> NonNull> { + NonNull::from(self.ptr) + } +} + +/// An owned tree. Note that despite being owned, this does not have a destructor, +/// and must be cleaned up manually. +pub struct Root { + node: BoxedNode, + height: usize +} + +unsafe impl Sync for Root { } +unsafe impl Send for Root { } + +impl Root { + pub fn is_shared_root(&self) -> bool { + self.as_ref().is_shared_root() + } + + pub fn shared_empty_root() -> Self { + Root { + node: unsafe { + BoxedNode::from_ptr(NonNull::new_unchecked( + &EMPTY_ROOT_NODE as *const _ as *const LeafNode as *mut _ + )) + }, + height: 0, + } + } + + pub fn new_leaf() -> Self { + Root { + node: BoxedNode::from_leaf(Box::new(unsafe { LeafNode::new() })), + height: 0 + } + } + + pub fn as_ref(&self) + -> NodeRef { + NodeRef { + height: self.height, + node: self.node.as_ptr(), + root: self as *const _ as *mut _, + _marker: PhantomData, + } + } + + pub fn as_mut(&mut self) + -> NodeRef { + NodeRef { + height: self.height, + node: self.node.as_ptr(), + root: self as *mut _, + _marker: PhantomData, + } + } + + pub fn into_ref(self) + -> NodeRef { + NodeRef { + height: self.height, + node: self.node.as_ptr(), + root: ptr::null_mut(), // FIXME: Is there anything better to do here? + _marker: PhantomData, + } + } + + /// Adds a new internal node with a single edge, pointing to the previous root, and make that + /// new node the root. This increases the height by 1 and is the opposite of `pop_level`. + pub fn push_level(&mut self) + -> NodeRef { + debug_assert!(!self.is_shared_root()); + let mut new_node = Box::new(unsafe { InternalNode::new() }); + new_node.edges[0] = unsafe { BoxedNode::from_ptr(self.node.as_ptr()) }; + + self.node = BoxedNode::from_internal(new_node); + self.height += 1; + + let mut ret = NodeRef { + height: self.height, + node: self.node.as_ptr(), + root: self as *mut _, + _marker: PhantomData + }; + + unsafe { + ret.reborrow_mut().first_edge().correct_parent_link(); + } + + ret + } + + /// Removes the root node, using its first child as the new root. This cannot be called when + /// the tree consists only of a leaf node. As it is intended only to be called when the root + /// has only one edge, no cleanup is done on any of the other children are elements of the root. + /// This decreases the height by 1 and is the opposite of `push_level`. + pub fn pop_level(&mut self) { + debug_assert!(self.height > 0); + + let top = self.node.ptr; + + self.node = unsafe { + BoxedNode::from_ptr(self.as_mut() + .cast_unchecked::() + .first_edge() + .descend() + .node) + }; + self.height -= 1; + self.as_mut().as_leaf_mut().parent = ptr::null(); + + unsafe { + Global.dealloc(NonNull::from(top).cast(), Layout::new::>()); + } + } +} + +// N.B. `NodeRef` is always covariant in `K` and `V`, even when the `BorrowType` +// is `Mut`. This is technically wrong, but cannot result in any unsafety due to +// internal use of `NodeRef` because we stay completely generic over `K` and `V`. +// However, whenever a public type wraps `NodeRef`, make sure that it has the +// correct variance. +/// A reference to a node. +/// +/// This type has a number of parameters that controls how it acts: +/// - `BorrowType`: This can be `Immut<'a>` or `Mut<'a>` for some `'a` or `Owned`. +/// When this is `Immut<'a>`, the `NodeRef` acts roughly like `&'a Node`, +/// when this is `Mut<'a>`, the `NodeRef` acts roughly like `&'a mut Node`, +/// and when this is `Owned`, the `NodeRef` acts roughly like `Box`. +/// - `K` and `V`: These control what types of things are stored in the nodes. +/// - `Type`: This can be `Leaf`, `Internal`, or `LeafOrInternal`. When this is +/// `Leaf`, the `NodeRef` points to a leaf node, when this is `Internal` the +/// `NodeRef` points to an internal node, and when this is `LeafOrInternal` the +/// `NodeRef` could be pointing to either type of node. +pub struct NodeRef { + height: usize, + node: NonNull>, + // This is null unless the borrow type is `Mut` + root: *const Root, + _marker: PhantomData<(BorrowType, Type)> +} + +impl<'a, K: 'a, V: 'a, Type> Copy for NodeRef, K, V, Type> { } +impl<'a, K: 'a, V: 'a, Type> Clone for NodeRef, K, V, Type> { + fn clone(&self) -> Self { + *self + } +} + +unsafe impl Sync + for NodeRef { } + +unsafe impl<'a, K: Sync + 'a, V: Sync + 'a, Type> Send + for NodeRef, K, V, Type> { } +unsafe impl<'a, K: Send + 'a, V: Send + 'a, Type> Send + for NodeRef, K, V, Type> { } +unsafe impl Send + for NodeRef { } + +impl NodeRef { + fn as_internal(&self) -> &InternalNode { + unsafe { + &*(self.node.as_ptr() as *mut InternalNode) + } + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + fn as_internal_mut(&mut self) -> &mut InternalNode { + unsafe { + &mut *(self.node.as_ptr() as *mut InternalNode) + } + } +} + + +impl NodeRef { + /// Finds the length of the node. This is the number of keys or values. In an + /// internal node, the number of edges is `len() + 1`. + pub fn len(&self) -> usize { + self.as_leaf().len as usize + } + + /// Returns the height of this node in the whole tree. Zero height denotes the + /// leaf level. + pub fn height(&self) -> usize { + self.height + } + + /// Removes any static information about whether this node is a `Leaf` or an + /// `Internal` node. + pub fn forget_type(self) -> NodeRef { + NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + } + } + + /// Temporarily takes out another, immutable reference to the same node. + fn reborrow<'a>(&'a self) -> NodeRef, K, V, Type> { + NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + } + } + + fn as_leaf(&self) -> &LeafNode { + unsafe { + self.node.as_ref() + } + } + + pub fn is_shared_root(&self) -> bool { + self.as_leaf().is_shared_root() + } + + pub fn keys(&self) -> &[K] { + self.reborrow().into_key_slice() + } + + fn vals(&self) -> &[V] { + self.reborrow().into_val_slice() + } + + /// Finds the parent of the current node. Returns `Ok(handle)` if the current + /// node actually has a parent, where `handle` points to the edge of the parent + /// that points to the current node. Returns `Err(self)` if the current node has + /// no parent, giving back the original `NodeRef`. + /// + /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should + /// both, upon success, do nothing. + pub fn ascend(self) -> Result< + Handle< + NodeRef< + BorrowType, + K, V, + marker::Internal + >, + marker::Edge + >, + Self + > { + let parent_as_leaf = self.as_leaf().parent as *const LeafNode; + if let Some(non_zero) = NonNull::new(parent_as_leaf as *mut _) { + Ok(Handle { + node: NodeRef { + height: self.height + 1, + node: non_zero, + root: self.root, + _marker: PhantomData + }, + idx: self.as_leaf().parent_idx as usize, + _marker: PhantomData + }) + } else { + Err(self) + } + } + + pub fn first_edge(self) -> Handle { + Handle::new_edge(self, 0) + } + + pub fn last_edge(self) -> Handle { + let len = self.len(); + Handle::new_edge(self, len) + } + + /// Note that `self` must be nonempty. + pub fn first_kv(self) -> Handle { + debug_assert!(self.len() > 0); + Handle::new_kv(self, 0) + } + + /// Note that `self` must be nonempty. + pub fn last_kv(self) -> Handle { + let len = self.len(); + debug_assert!(len > 0); + Handle::new_kv(self, len - 1) + } +} + +impl NodeRef { + /// Similar to `ascend`, gets a reference to a node's parent node, but also + /// deallocate the current node in the process. This is unsafe because the + /// current node will still be accessible despite being deallocated. + pub unsafe fn deallocate_and_ascend(self) -> Option< + Handle< + NodeRef< + marker::Owned, + K, V, + marker::Internal + >, + marker::Edge + > + > { + debug_assert!(!self.is_shared_root()); + let node = self.node; + let ret = self.ascend().ok(); + Global.dealloc(node.cast(), Layout::new::>()); + ret + } +} + +impl NodeRef { + /// Similar to `ascend`, gets a reference to a node's parent node, but also + /// deallocate the current node in the process. This is unsafe because the + /// current node will still be accessible despite being deallocated. + pub unsafe fn deallocate_and_ascend(self) -> Option< + Handle< + NodeRef< + marker::Owned, + K, V, + marker::Internal + >, + marker::Edge + > + > { + let node = self.node; + let ret = self.ascend().ok(); + Global.dealloc(node.cast(), Layout::new::>()); + ret + } +} + +impl<'a, K, V, Type> NodeRef, K, V, Type> { + /// Unsafely asserts to the compiler some static information about whether this + /// node is a `Leaf`. + unsafe fn cast_unchecked(&mut self) + -> NodeRef { + + NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + } + } + + /// Temporarily takes out another, mutable reference to the same node. Beware, as + /// this method is very dangerous, doubly so since it may not immediately appear + /// dangerous. + /// + /// Because mutable pointers can roam anywhere around the tree and can even (through + /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` + /// can easily be used to make the original mutable pointer dangling, or, in the case + /// of a reborrowed handle, out of bounds. + // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts + // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. + unsafe fn reborrow_mut(&mut self) -> NodeRef { + NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + } + } + + fn as_leaf_mut(&mut self) -> &mut LeafNode { + unsafe { + self.node.as_mut() + } + } + + fn keys_mut(&mut self) -> &mut [K] { + unsafe { self.reborrow_mut().into_key_slice_mut() } + } + + fn vals_mut(&mut self) -> &mut [V] { + unsafe { self.reborrow_mut().into_val_slice_mut() } + } +} + +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + fn into_key_slice(self) -> &'a [K] { + // When taking a pointer to the keys, if our key has a stricter + // alignment requirement than the shared root does, then the pointer + // would be out of bounds, which LLVM assumes will not happen. If the + // alignment is more strict, we need to make an empty slice that doesn't + // use an out of bounds pointer. + if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + &[] + } else { + // Here either it's not the root, or the alignment is less strict, + // in which case the keys pointer will point "one-past-the-end" of + // the node, which is allowed by LLVM. + unsafe { + slice::from_raw_parts( + self.as_leaf().keys.as_ptr(), + self.len() + ) + } + } + } + + fn into_val_slice(self) -> &'a [V] { + debug_assert!(!self.is_shared_root()); + unsafe { + slice::from_raw_parts( + self.as_leaf().vals.as_ptr(), + self.len() + ) + } + } + + fn into_slices(self) -> (&'a [K], &'a [V]) { + let k = unsafe { ptr::read(&self) }; + (k.into_key_slice(), self.into_val_slice()) + } +} + +impl<'a, K: 'a, V: 'a, Type> NodeRef, K, V, Type> { + /// Gets a mutable reference to the root itself. This is useful primarily when the + /// height of the tree needs to be adjusted. Never call this on a reborrowed pointer. + pub fn into_root_mut(self) -> &'a mut Root { + unsafe { + &mut *(self.root as *mut Root) + } + } + + fn into_key_slice_mut(mut self) -> &'a mut [K] { + if mem::align_of::() > mem::align_of::>() && self.is_shared_root() { + &mut [] + } else { + unsafe { + slice::from_raw_parts_mut( + &mut self.as_leaf_mut().keys as *mut [K] as *mut K, + self.len() + ) + } + } + } + + fn into_val_slice_mut(mut self) -> &'a mut [V] { + debug_assert!(!self.is_shared_root()); + unsafe { + slice::from_raw_parts_mut( + &mut self.as_leaf_mut().vals as *mut [V] as *mut V, + self.len() + ) + } + } + + fn into_slices_mut(self) -> (&'a mut [K], &'a mut [V]) { + let k = unsafe { ptr::read(&self) }; + (k.into_key_slice_mut(), self.into_val_slice_mut()) + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Leaf> { + /// Adds a key/value pair the end of the node. + pub fn push(&mut self, key: K, val: V) { + // Necessary for correctness, but this is an internal module + debug_assert!(self.len() < CAPACITY); + debug_assert!(!self.is_shared_root()); + + let idx = self.len(); + + unsafe { + ptr::write(self.keys_mut().get_unchecked_mut(idx), key); + ptr::write(self.vals_mut().get_unchecked_mut(idx), val); + } + + self.as_leaf_mut().len += 1; + } + + /// Adds a key/value pair to the beginning of the node. + pub fn push_front(&mut self, key: K, val: V) { + // Necessary for correctness, but this is an internal module + debug_assert!(self.len() < CAPACITY); + debug_assert!(!self.is_shared_root()); + + unsafe { + slice_insert(self.keys_mut(), 0, key); + slice_insert(self.vals_mut(), 0, val); + } + + self.as_leaf_mut().len += 1; + } +} + +impl<'a, K, V> NodeRef, K, V, marker::Internal> { + /// Adds a key/value pair and an edge to go to the right of that pair to + /// the end of the node. + pub fn push(&mut self, key: K, val: V, edge: Root) { + // Necessary for correctness, but this is an internal module + debug_assert!(edge.height == self.height - 1); + debug_assert!(self.len() < CAPACITY); + + let idx = self.len(); + + unsafe { + ptr::write(self.keys_mut().get_unchecked_mut(idx), key); + ptr::write(self.vals_mut().get_unchecked_mut(idx), val); + ptr::write(self.as_internal_mut().edges.get_unchecked_mut(idx + 1), edge.node); + + self.as_leaf_mut().len += 1; + + Handle::new_edge(self.reborrow_mut(), idx + 1).correct_parent_link(); + } + } + + fn correct_childrens_parent_links(&mut self, first: usize, after_last: usize) { + for i in first..after_last { + Handle::new_edge(unsafe { self.reborrow_mut() }, i).correct_parent_link(); + } + } + + fn correct_all_childrens_parent_links(&mut self) { + let len = self.len(); + self.correct_childrens_parent_links(0, len + 1); + } + + /// Adds a key/value pair and an edge to go to the left of that pair to + /// the beginning of the node. + pub fn push_front(&mut self, key: K, val: V, edge: Root) { + // Necessary for correctness, but this is an internal module + debug_assert!(edge.height == self.height - 1); + debug_assert!(self.len() < CAPACITY); + + unsafe { + slice_insert(self.keys_mut(), 0, key); + slice_insert(self.vals_mut(), 0, val); + slice_insert( + slice::from_raw_parts_mut( + self.as_internal_mut().edges.as_mut_ptr(), + self.len()+1 + ), + 0, + edge.node + ); + + self.as_leaf_mut().len += 1; + + self.correct_all_childrens_parent_links(); + } + } +} + +impl<'a, K, V> NodeRef, K, V, marker::LeafOrInternal> { + /// Removes a key/value pair from the end of this node. If this is an internal node, + /// also removes the edge that was to the right of that pair. + pub fn pop(&mut self) -> (K, V, Option>) { + // Necessary for correctness, but this is an internal module + debug_assert!(self.len() > 0); + + let idx = self.len() - 1; + + unsafe { + let key = ptr::read(self.keys().get_unchecked(idx)); + let val = ptr::read(self.vals().get_unchecked(idx)); + let edge = match self.reborrow_mut().force() { + ForceResult::Leaf(_) => None, + ForceResult::Internal(internal) => { + let edge = ptr::read(internal.as_internal().edges.get_unchecked(idx + 1)); + let mut new_root = Root { node: edge, height: internal.height - 1 }; + new_root.as_mut().as_leaf_mut().parent = ptr::null(); + Some(new_root) + } + }; + + self.as_leaf_mut().len -= 1; + (key, val, edge) + } + } + + /// Removes a key/value pair from the beginning of this node. If this is an internal node, + /// also removes the edge that was to the left of that pair. + pub fn pop_front(&mut self) -> (K, V, Option>) { + // Necessary for correctness, but this is an internal module + debug_assert!(self.len() > 0); + + let old_len = self.len(); + + unsafe { + let key = slice_remove(self.keys_mut(), 0); + let val = slice_remove(self.vals_mut(), 0); + let edge = match self.reborrow_mut().force() { + ForceResult::Leaf(_) => None, + ForceResult::Internal(mut internal) => { + let edge = slice_remove( + slice::from_raw_parts_mut( + internal.as_internal_mut().edges.as_mut_ptr(), + old_len+1 + ), + 0 + ); + + let mut new_root = Root { node: edge, height: internal.height - 1 }; + new_root.as_mut().as_leaf_mut().parent = ptr::null(); + + for i in 0..old_len { + Handle::new_edge(internal.reborrow_mut(), i).correct_parent_link(); + } + + Some(new_root) + } + }; + + self.as_leaf_mut().len -= 1; + + (key, val, edge) + } + } + + fn into_kv_pointers_mut(mut self) -> (*mut K, *mut V) { + ( + self.keys_mut().as_mut_ptr(), + self.vals_mut().as_mut_ptr() + ) + } +} + +impl NodeRef { + /// Checks whether a node is an `Internal` node or a `Leaf` node. + pub fn force(self) -> ForceResult< + NodeRef, + NodeRef + > { + if self.height == 0 { + ForceResult::Leaf(NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + }) + } else { + ForceResult::Internal(NodeRef { + height: self.height, + node: self.node, + root: self.root, + _marker: PhantomData + }) + } + } +} + +/// A reference to a specific key/value pair or edge within a node. The `Node` parameter +/// must be a `NodeRef`, while the `Type` can either be `KV` (signifying a handle on a key/value +/// pair) or `Edge` (signifying a handle on an edge). +/// +/// Note that even `Leaf` nodes can have `Edge` handles. Instead of representing a pointer to +/// a child node, these represent the spaces where child pointers would go between the key/value +/// pairs. For example, in a node with length 2, there would be 3 possible edge locations - one +/// to the left of the node, one between the two pairs, and one at the right of the node. +pub struct Handle { + node: Node, + idx: usize, + _marker: PhantomData +} + +impl Copy for Handle { } +// We don't need the full generality of `#[derive(Clone)]`, as the only time `Node` will be +// `Clone`able is when it is an immutable reference and therefore `Copy`. +impl Clone for Handle { + fn clone(&self) -> Self { + *self + } +} + +impl Handle { + /// Retrieves the node that contains the edge of key/value pair this handle points to. + pub fn into_node(self) -> Node { + self.node + } +} + +impl Handle, marker::KV> { + /// Creates a new handle to a key/value pair in `node`. `idx` must be less than `node.len()`. + pub fn new_kv(node: NodeRef, idx: usize) -> Self { + // Necessary for correctness, but in a private module + debug_assert!(idx < node.len()); + + Handle { + node, + idx, + _marker: PhantomData + } + } + + pub fn left_edge(self) -> Handle, marker::Edge> { + Handle::new_edge(self.node, self.idx) + } + + pub fn right_edge(self) -> Handle, marker::Edge> { + Handle::new_edge(self.node, self.idx + 1) + } +} + +impl PartialEq + for Handle, HandleType> { + + fn eq(&self, other: &Self) -> bool { + self.node.node == other.node.node && self.idx == other.idx + } +} + +impl + Handle, HandleType> { + + /// Temporarily takes out another, immutable handle on the same location. + pub fn reborrow(&self) + -> Handle, HandleType> { + + // We can't use Handle::new_kv or Handle::new_edge because we don't know our type + Handle { + node: self.node.reborrow(), + idx: self.idx, + _marker: PhantomData + } + } +} + +impl<'a, K, V, NodeType, HandleType> + Handle, K, V, NodeType>, HandleType> { + + /// Temporarily takes out another, mutable handle on the same location. Beware, as + /// this method is very dangerous, doubly so since it may not immediately appear + /// dangerous. + /// + /// Because mutable pointers can roam anywhere around the tree and can even (through + /// `into_root_mut`) mess with the root of the tree, the result of `reborrow_mut` + /// can easily be used to make the original mutable pointer dangling, or, in the case + /// of a reborrowed handle, out of bounds. + // FIXME(@gereeter) consider adding yet another type parameter to `NodeRef` that restricts + // the use of `ascend` and `into_root_mut` on reborrowed pointers, preventing this unsafety. + pub unsafe fn reborrow_mut(&mut self) + -> Handle, HandleType> { + + // We can't use Handle::new_kv or Handle::new_edge because we don't know our type + Handle { + node: self.node.reborrow_mut(), + idx: self.idx, + _marker: PhantomData + } + } +} + +impl + Handle, marker::Edge> { + + /// Creates a new handle to an edge in `node`. `idx` must be less than or equal to + /// `node.len()`. + pub fn new_edge(node: NodeRef, idx: usize) -> Self { + // Necessary for correctness, but in a private module + debug_assert!(idx <= node.len()); + + Handle { + node, + idx, + _marker: PhantomData + } + } + + pub fn left_kv(self) + -> Result, marker::KV>, Self> { + + if self.idx > 0 { + Ok(Handle::new_kv(self.node, self.idx - 1)) + } else { + Err(self) + } + } + + pub fn right_kv(self) + -> Result, marker::KV>, Self> { + + if self.idx < self.node.len() { + Ok(Handle::new_kv(self.node, self.idx)) + } else { + Err(self) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::Edge> { + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method assumes that there is enough space in the node for the new + /// pair to fit. + /// + /// The returned pointer points to the inserted value. + fn insert_fit(&mut self, key: K, val: V) -> *mut V { + // Necessary for correctness, but in a private module + debug_assert!(self.node.len() < CAPACITY); + debug_assert!(!self.node.is_shared_root()); + + unsafe { + slice_insert(self.node.keys_mut(), self.idx, key); + slice_insert(self.node.vals_mut(), self.idx, val); + + self.node.as_leaf_mut().len += 1; + + self.node.vals_mut().get_unchecked_mut(self.idx) + } + } + + /// Inserts a new key/value pair between the key/value pairs to the right and left of + /// this edge. This method splits the node if there isn't enough room. + /// + /// The returned pointer points to the inserted value. + pub fn insert(mut self, key: K, val: V) + -> (InsertResult<'a, K, V, marker::Leaf>, *mut V) { + + if self.node.len() < CAPACITY { + let ptr = self.insert_fit(key, val); + (InsertResult::Fit(Handle::new_kv(self.node, self.idx)), ptr) + } else { + let middle = Handle::new_kv(self.node, B); + let (mut left, k, v, mut right) = middle.split(); + let ptr = if self.idx <= B { + unsafe { + Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val) + } + } else { + unsafe { + Handle::new_edge( + right.as_mut().cast_unchecked::(), + self.idx - (B + 1) + ).insert_fit(key, val) + } + }; + (InsertResult::Split(left, k, v, right), ptr) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Internal>, marker::Edge> { + /// Fixes the parent pointer and index in the child node below this edge. This is useful + /// when the ordering of edges has been changed, such as in the various `insert` methods. + fn correct_parent_link(mut self) { + let idx = self.idx as u16; + let ptr = self.node.as_internal_mut() as *mut _; + let mut child = self.descend(); + child.as_leaf_mut().parent = ptr; + child.as_leaf_mut().parent_idx = idx; + } + + /// Unsafely asserts to the compiler some static information about whether the underlying + /// node of this handle is a `Leaf`. + unsafe fn cast_unchecked(&mut self) + -> Handle, marker::Edge> { + + Handle::new_edge(self.node.cast_unchecked(), self.idx) + } + + /// Inserts a new key/value pair and an edge that will go to the right of that new pair + /// between this edge and the key/value pair to the right of this edge. This method assumes + /// that there is enough space in the node for the new pair to fit. + fn insert_fit(&mut self, key: K, val: V, edge: Root) { + // Necessary for correctness, but in an internal module + debug_assert!(self.node.len() < CAPACITY); + debug_assert!(edge.height == self.node.height - 1); + + unsafe { + // This cast is a lie, but it allows us to reuse the key/value insertion logic. + self.cast_unchecked::().insert_fit(key, val); + + slice_insert( + slice::from_raw_parts_mut( + self.node.as_internal_mut().edges.as_mut_ptr(), + self.node.len() + ), + self.idx + 1, + edge.node + ); + + for i in (self.idx+1)..(self.node.len()+1) { + Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); + } + } + } + + /// Inserts a new key/value pair and an edge that will go to the right of that new pair + /// between this edge and the key/value pair to the right of this edge. This method splits + /// the node if there isn't enough room. + pub fn insert(mut self, key: K, val: V, edge: Root) + -> InsertResult<'a, K, V, marker::Internal> { + + // Necessary for correctness, but this is an internal module + debug_assert!(edge.height == self.node.height - 1); + + if self.node.len() < CAPACITY { + self.insert_fit(key, val, edge); + InsertResult::Fit(Handle::new_kv(self.node, self.idx)) + } else { + let middle = Handle::new_kv(self.node, B); + let (mut left, k, v, mut right) = middle.split(); + if self.idx <= B { + unsafe { + Handle::new_edge(left.reborrow_mut(), self.idx).insert_fit(key, val, edge); + } + } else { + unsafe { + Handle::new_edge( + right.as_mut().cast_unchecked::(), + self.idx - (B + 1) + ).insert_fit(key, val, edge); + } + } + InsertResult::Split(left, k, v, right) + } + } +} + +impl + Handle, marker::Edge> { + + /// Finds the node pointed to by this edge. + /// + /// `edge.descend().ascend().unwrap()` and `node.ascend().unwrap().descend()` should + /// both, upon success, do nothing. + pub fn descend(self) -> NodeRef { + NodeRef { + height: self.node.height - 1, + node: unsafe { self.node.as_internal().edges.get_unchecked(self.idx).as_ptr() }, + root: self.node.root, + _marker: PhantomData + } + } +} + +impl<'a, K: 'a, V: 'a, NodeType> + Handle, K, V, NodeType>, marker::KV> { + + pub fn into_kv(self) -> (&'a K, &'a V) { + let (keys, vals) = self.node.into_slices(); + unsafe { + (keys.get_unchecked(self.idx), vals.get_unchecked(self.idx)) + } + } +} + +impl<'a, K: 'a, V: 'a, NodeType> + Handle, K, V, NodeType>, marker::KV> { + + pub fn into_kv_mut(self) -> (&'a mut K, &'a mut V) { + let (keys, vals) = self.node.into_slices_mut(); + unsafe { + (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) + } + } +} + +impl<'a, K, V, NodeType> Handle, K, V, NodeType>, marker::KV> { + pub fn kv_mut(&mut self) -> (&mut K, &mut V) { + unsafe { + let (keys, vals) = self.node.reborrow_mut().into_slices_mut(); + (keys.get_unchecked_mut(self.idx), vals.get_unchecked_mut(self.idx)) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Leaf>, marker::KV> { + /// Splits the underlying node into three parts: + /// + /// - The node is truncated to only contain the key/value pairs to the right of + /// this handle. + /// - The key and value pointed to by this handle and extracted. + /// - All the key/value pairs to the right of this handle are put into a newly + /// allocated node. + pub fn split(mut self) + -> (NodeRef, K, V, marker::Leaf>, K, V, Root) { + debug_assert!(!self.node.is_shared_root()); + unsafe { + let mut new_node = Box::new(LeafNode::new()); + + let k = ptr::read(self.node.keys().get_unchecked(self.idx)); + let v = ptr::read(self.node.vals().get_unchecked(self.idx)); + + let new_len = self.node.len() - self.idx - 1; + + ptr::copy_nonoverlapping( + self.node.keys().as_ptr().offset(self.idx as isize + 1), + new_node.keys.as_mut_ptr(), + new_len + ); + ptr::copy_nonoverlapping( + self.node.vals().as_ptr().offset(self.idx as isize + 1), + new_node.vals.as_mut_ptr(), + new_len + ); + + self.node.as_leaf_mut().len = self.idx as u16; + new_node.len = new_len as u16; + + ( + self.node, + k, v, + Root { + node: BoxedNode::from_leaf(new_node), + height: 0 + } + ) + } + } + + /// Removes the key/value pair pointed to by this handle, returning the edge between the + /// now adjacent key/value pairs to the left and right of this handle. + pub fn remove(mut self) + -> (Handle, K, V, marker::Leaf>, marker::Edge>, K, V) { + debug_assert!(!self.node.is_shared_root()); + unsafe { + let k = slice_remove(self.node.keys_mut(), self.idx); + let v = slice_remove(self.node.vals_mut(), self.idx); + self.node.as_leaf_mut().len -= 1; + (self.left_edge(), k, v) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::Internal>, marker::KV> { + /// Splits the underlying node into three parts: + /// + /// - The node is truncated to only contain the edges and key/value pairs to the + /// right of this handle. + /// - The key and value pointed to by this handle and extracted. + /// - All the edges and key/value pairs to the right of this handle are put into + /// a newly allocated node. + pub fn split(mut self) + -> (NodeRef, K, V, marker::Internal>, K, V, Root) { + unsafe { + let mut new_node = Box::new(InternalNode::new()); + + let k = ptr::read(self.node.keys().get_unchecked(self.idx)); + let v = ptr::read(self.node.vals().get_unchecked(self.idx)); + + let height = self.node.height; + let new_len = self.node.len() - self.idx - 1; + + ptr::copy_nonoverlapping( + self.node.keys().as_ptr().offset(self.idx as isize + 1), + new_node.data.keys.as_mut_ptr(), + new_len + ); + ptr::copy_nonoverlapping( + self.node.vals().as_ptr().offset(self.idx as isize + 1), + new_node.data.vals.as_mut_ptr(), + new_len + ); + ptr::copy_nonoverlapping( + self.node.as_internal().edges.as_ptr().offset(self.idx as isize + 1), + new_node.edges.as_mut_ptr(), + new_len + 1 + ); + + self.node.as_leaf_mut().len = self.idx as u16; + new_node.data.len = new_len as u16; + + let mut new_root = Root { + node: BoxedNode::from_internal(new_node), + height, + }; + + for i in 0..(new_len+1) { + Handle::new_edge(new_root.as_mut().cast_unchecked(), i).correct_parent_link(); + } + + ( + self.node, + k, v, + new_root + ) + } + } + + /// Returns whether it is valid to call `.merge()`, i.e., whether there is enough room in + /// a node to hold the combination of the nodes to the left and right of this handle along + /// with the key/value pair at this handle. + pub fn can_merge(&self) -> bool { + ( + self.reborrow() + .left_edge() + .descend() + .len() + + self.reborrow() + .right_edge() + .descend() + .len() + + 1 + ) <= CAPACITY + } + + /// Combines the node immediately to the left of this handle, the key/value pair pointed + /// to by this handle, and the node immediately to the right of this handle into one new + /// child of the underlying node, returning an edge referencing that new child. + /// + /// Assumes that this edge `.can_merge()`. + pub fn merge(mut self) + -> Handle, K, V, marker::Internal>, marker::Edge> { + let self1 = unsafe { ptr::read(&self) }; + let self2 = unsafe { ptr::read(&self) }; + let mut left_node = self1.left_edge().descend(); + let left_len = left_node.len(); + let mut right_node = self2.right_edge().descend(); + let right_len = right_node.len(); + + // necessary for correctness, but in a private module + debug_assert!(left_len + right_len + 1 <= CAPACITY); + + unsafe { + ptr::write(left_node.keys_mut().get_unchecked_mut(left_len), + slice_remove(self.node.keys_mut(), self.idx)); + ptr::copy_nonoverlapping( + right_node.keys().as_ptr(), + left_node.keys_mut().as_mut_ptr().offset(left_len as isize + 1), + right_len + ); + ptr::write(left_node.vals_mut().get_unchecked_mut(left_len), + slice_remove(self.node.vals_mut(), self.idx)); + ptr::copy_nonoverlapping( + right_node.vals().as_ptr(), + left_node.vals_mut().as_mut_ptr().offset(left_len as isize + 1), + right_len + ); + + slice_remove(&mut self.node.as_internal_mut().edges, self.idx + 1); + for i in self.idx+1..self.node.len() { + Handle::new_edge(self.node.reborrow_mut(), i).correct_parent_link(); + } + self.node.as_leaf_mut().len -= 1; + + left_node.as_leaf_mut().len += right_len as u16 + 1; + + if self.node.height > 1 { + ptr::copy_nonoverlapping( + right_node.cast_unchecked().as_internal().edges.as_ptr(), + left_node.cast_unchecked() + .as_internal_mut() + .edges + .as_mut_ptr() + .offset(left_len as isize + 1), + right_len + 1 + ); + + for i in left_len+1..left_len+right_len+2 { + Handle::new_edge( + left_node.cast_unchecked().reborrow_mut(), + i + ).correct_parent_link(); + } + + Global.dealloc( + right_node.node.cast(), + Layout::new::>(), + ); + } else { + Global.dealloc( + right_node.node.cast(), + Layout::new::>(), + ); + } + + Handle::new_edge(self.node, self.idx) + } + } + + /// This removes a key/value pair from the left child and replaces it with the key/value pair + /// pointed to by this handle while pushing the old key/value pair of this handle into the right + /// child. + pub fn steal_left(&mut self) { + unsafe { + let (k, v, edge) = self.reborrow_mut().left_edge().descend().pop(); + + let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); + let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); + + match self.reborrow_mut().right_edge().descend().force() { + ForceResult::Leaf(mut leaf) => leaf.push_front(k, v), + ForceResult::Internal(mut internal) => internal.push_front(k, v, edge.unwrap()) + } + } + } + + /// This removes a key/value pair from the right child and replaces it with the key/value pair + /// pointed to by this handle while pushing the old key/value pair of this handle into the left + /// child. + pub fn steal_right(&mut self) { + unsafe { + let (k, v, edge) = self.reborrow_mut().right_edge().descend().pop_front(); + + let k = mem::replace(self.reborrow_mut().into_kv_mut().0, k); + let v = mem::replace(self.reborrow_mut().into_kv_mut().1, v); + + match self.reborrow_mut().left_edge().descend().force() { + ForceResult::Leaf(mut leaf) => leaf.push(k, v), + ForceResult::Internal(mut internal) => internal.push(k, v, edge.unwrap()) + } + } + } + + /// This does stealing similar to `steal_left` but steals multiple elements at once. + pub fn bulk_steal_left(&mut self, count: usize) { + unsafe { + let mut left_node = ptr::read(self).left_edge().descend(); + let left_len = left_node.len(); + let mut right_node = ptr::read(self).right_edge().descend(); + let right_len = right_node.len(); + + // Make sure that we may steal safely. + debug_assert!(right_len + count <= CAPACITY); + debug_assert!(left_len >= count); + + let new_left_len = left_len - count; + + // Move data. + { + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + let parent_kv = { + let kv = self.reborrow_mut().into_kv_mut(); + (kv.0 as *mut K, kv.1 as *mut V) + }; + + // Make room for stolen elements in the right child. + ptr::copy(right_kv.0, + right_kv.0.offset(count as isize), + right_len); + ptr::copy(right_kv.1, + right_kv.1.offset(count as isize), + right_len); + + // Move elements from the left child to the right one. + move_kv(left_kv, new_left_len + 1, right_kv, 0, count - 1); + + // Move parent's key/value pair to the right child. + move_kv(parent_kv, 0, right_kv, count - 1, 1); + + // Move the left-most stolen pair to the parent. + move_kv(left_kv, new_left_len, parent_kv, 0, 1); + } + + left_node.reborrow_mut().as_leaf_mut().len -= count as u16; + right_node.reborrow_mut().as_leaf_mut().len += count as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + // Make room for stolen edges. + let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + ptr::copy(right_edges, + right_edges.offset(count as isize), + right_len + 1); + right.correct_childrens_parent_links(count, count + right_len + 1); + + move_edges(left, new_left_len + 1, right, 0, count); + }, + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } + _ => { unreachable!(); } + } + } + } + + /// The symmetric clone of `bulk_steal_left`. + pub fn bulk_steal_right(&mut self, count: usize) { + unsafe { + let mut left_node = ptr::read(self).left_edge().descend(); + let left_len = left_node.len(); + let mut right_node = ptr::read(self).right_edge().descend(); + let right_len = right_node.len(); + + // Make sure that we may steal safely. + debug_assert!(left_len + count <= CAPACITY); + debug_assert!(right_len >= count); + + let new_right_len = right_len - count; + + // Move data. + { + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + let parent_kv = { + let kv = self.reborrow_mut().into_kv_mut(); + (kv.0 as *mut K, kv.1 as *mut V) + }; + + // Move parent's key/value pair to the left child. + move_kv(parent_kv, 0, left_kv, left_len, 1); + + // Move elements from the right child to the left one. + move_kv(right_kv, 0, left_kv, left_len + 1, count - 1); + + // Move the right-most stolen pair to the parent. + move_kv(right_kv, count - 1, parent_kv, 0, 1); + + // Fix right indexing + ptr::copy(right_kv.0.offset(count as isize), + right_kv.0, + new_right_len); + ptr::copy(right_kv.1.offset(count as isize), + right_kv.1, + new_right_len); + } + + left_node.reborrow_mut().as_leaf_mut().len += count as u16; + right_node.reborrow_mut().as_leaf_mut().len -= count as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(mut right)) => { + move_edges(right.reborrow_mut(), 0, left, left_len + 1, count); + + // Fix right indexing. + let right_edges = right.reborrow_mut().as_internal_mut().edges.as_mut_ptr(); + ptr::copy(right_edges.offset(count as isize), + right_edges, + new_right_len + 1); + right.correct_childrens_parent_links(0, new_right_len + 1); + }, + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } + _ => { unreachable!(); } + } + } + } +} + +unsafe fn move_kv( + source: (*mut K, *mut V), source_offset: usize, + dest: (*mut K, *mut V), dest_offset: usize, + count: usize) +{ + ptr::copy_nonoverlapping(source.0.offset(source_offset as isize), + dest.0.offset(dest_offset as isize), + count); + ptr::copy_nonoverlapping(source.1.offset(source_offset as isize), + dest.1.offset(dest_offset as isize), + count); +} + +// Source and destination must have the same height. +unsafe fn move_edges( + mut source: NodeRef, source_offset: usize, + mut dest: NodeRef, dest_offset: usize, + count: usize) +{ + let source_ptr = source.as_internal_mut().edges.as_mut_ptr(); + let dest_ptr = dest.as_internal_mut().edges.as_mut_ptr(); + ptr::copy_nonoverlapping(source_ptr.offset(source_offset as isize), + dest_ptr.offset(dest_offset as isize), + count); + dest.correct_childrens_parent_links(dest_offset, dest_offset + count); +} + +impl + Handle, HandleType> { + + /// Check whether the underlying node is an `Internal` node or a `Leaf` node. + pub fn force(self) -> ForceResult< + Handle, HandleType>, + Handle, HandleType> + > { + match self.node.force() { + ForceResult::Leaf(node) => ForceResult::Leaf(Handle { + node, + idx: self.idx, + _marker: PhantomData + }), + ForceResult::Internal(node) => ForceResult::Internal(Handle { + node, + idx: self.idx, + _marker: PhantomData + }) + } + } +} + +impl<'a, K, V> Handle, K, V, marker::LeafOrInternal>, marker::Edge> { + /// Move the suffix after `self` from one node to another one. `right` must be empty. + /// The first edge of `right` remains unchanged. + pub fn move_suffix(&mut self, + right: &mut NodeRef, K, V, marker::LeafOrInternal>) { + unsafe { + let left_new_len = self.idx; + let mut left_node = self.reborrow_mut().into_node(); + + let right_new_len = left_node.len() - left_new_len; + let mut right_node = right.reborrow_mut(); + + debug_assert!(right_node.len() == 0); + debug_assert!(left_node.height == right_node.height); + + let left_kv = left_node.reborrow_mut().into_kv_pointers_mut(); + let right_kv = right_node.reborrow_mut().into_kv_pointers_mut(); + + + move_kv(left_kv, left_new_len, right_kv, 0, right_new_len); + + left_node.reborrow_mut().as_leaf_mut().len = left_new_len as u16; + right_node.reborrow_mut().as_leaf_mut().len = right_new_len as u16; + + match (left_node.force(), right_node.force()) { + (ForceResult::Internal(left), ForceResult::Internal(right)) => { + move_edges(left, left_new_len + 1, right, 1, right_new_len); + }, + (ForceResult::Leaf(_), ForceResult::Leaf(_)) => { } + _ => { unreachable!(); } + } + } + } +} + +pub enum ForceResult { + Leaf(Leaf), + Internal(Internal) +} + +pub enum InsertResult<'a, K, V, Type> { + Fit(Handle, K, V, Type>, marker::KV>), + Split(NodeRef, K, V, Type>, K, V, Root) +} + +pub mod marker { + use core::marker::PhantomData; + + pub enum Leaf { } + pub enum Internal { } + pub enum LeafOrInternal { } + + pub enum Owned { } + pub struct Immut<'a>(PhantomData<&'a ()>); + pub struct Mut<'a>(PhantomData<&'a mut ()>); + + pub enum KV { } + pub enum Edge { } +} + +unsafe fn slice_insert(slice: &mut [T], idx: usize, val: T) { + ptr::copy( + slice.as_ptr().offset(idx as isize), + slice.as_mut_ptr().offset(idx as isize + 1), + slice.len() - idx + ); + ptr::write(slice.get_unchecked_mut(idx), val); +} + +unsafe fn slice_remove(slice: &mut [T], idx: usize) -> T { + let ret = ptr::read(slice.get_unchecked(idx)); + ptr::copy( + slice.as_ptr().offset(idx as isize + 1), + slice.as_mut_ptr().offset(idx as isize), + slice.len() - idx - 1 + ); + ret +} diff --git a/src/liballoc/collections/btree/search.rs b/src/liballoc/collections/btree/search.rs new file mode 100644 index 00000000000..bc1272fbc78 --- /dev/null +++ b/src/liballoc/collections/btree/search.rs @@ -0,0 +1,75 @@ +// Copyright 2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use core::cmp::Ordering; + +use borrow::Borrow; + +use super::node::{Handle, NodeRef, marker}; + +use super::node::ForceResult::*; +use self::SearchResult::*; + +pub enum SearchResult { + Found(Handle, marker::KV>), + GoDown(Handle, marker::Edge>) +} + +pub fn search_tree( + mut node: NodeRef, + key: &Q +) -> SearchResult + where Q: Ord, K: Borrow { + + loop { + match search_node(node, key) { + Found(handle) => return Found(handle), + GoDown(handle) => match handle.force() { + Leaf(leaf) => return GoDown(leaf), + Internal(internal) => { + node = internal.descend(); + continue; + } + } + } + } +} + +pub fn search_node( + node: NodeRef, + key: &Q +) -> SearchResult + where Q: Ord, K: Borrow { + + match search_linear(&node, key) { + (idx, true) => Found( + Handle::new_kv(node, idx) + ), + (idx, false) => SearchResult::GoDown( + Handle::new_edge(node, idx) + ) + } +} + +pub fn search_linear( + node: &NodeRef, + key: &Q +) -> (usize, bool) + where Q: Ord, K: Borrow { + + for (i, k) in node.keys().iter().enumerate() { + match key.cmp(k.borrow()) { + Ordering::Greater => {}, + Ordering::Equal => return (i, true), + Ordering::Less => return (i, false) + } + } + (node.keys().len(), false) +} diff --git a/src/liballoc/collections/btree/set.rs b/src/liballoc/collections/btree/set.rs new file mode 100644 index 00000000000..af9a7074e4a --- /dev/null +++ b/src/liballoc/collections/btree/set.rs @@ -0,0 +1,1153 @@ +// Copyright 2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +// This is pretty much entirely stolen from TreeSet, since BTreeMap has an identical interface +// to TreeMap + +use core::cmp::Ordering::{self, Less, Greater, Equal}; +use core::cmp::{min, max}; +use core::fmt::Debug; +use core::fmt; +use core::iter::{Peekable, FromIterator, FusedIterator}; +use core::ops::{BitOr, BitAnd, BitXor, Sub, RangeBounds}; + +use borrow::Borrow; +use collections::btree_map::{self, BTreeMap, Keys}; +use super::Recover; + +// FIXME(conventions): implement bounded iterators + +/// A set based on a B-Tree. +/// +/// See [`BTreeMap`]'s documentation for a detailed discussion of this collection's performance +/// benefits and drawbacks. +/// +/// It is a logic error for an item to be modified in such a way that the item's ordering relative +/// to any other item, as determined by the [`Ord`] trait, changes while it is in the set. This is +/// normally only possible through [`Cell`], [`RefCell`], global state, I/O, or unsafe code. +/// +/// [`BTreeMap`]: struct.BTreeMap.html +/// [`Ord`]: ../../std/cmp/trait.Ord.html +/// [`Cell`]: ../../std/cell/struct.Cell.html +/// [`RefCell`]: ../../std/cell/struct.RefCell.html +/// +/// # Examples +/// +/// ``` +/// use std::collections::BTreeSet; +/// +/// // Type inference lets us omit an explicit type signature (which +/// // would be `BTreeSet<&str>` in this example). +/// let mut books = BTreeSet::new(); +/// +/// // Add some books. +/// books.insert("A Dance With Dragons"); +/// books.insert("To Kill a Mockingbird"); +/// books.insert("The Odyssey"); +/// books.insert("The Great Gatsby"); +/// +/// // Check for a specific one. +/// if !books.contains("The Winds of Winter") { +/// println!("We have {} books, but The Winds of Winter ain't one.", +/// books.len()); +/// } +/// +/// // Remove a book. +/// books.remove("The Odyssey"); +/// +/// // Iterate over everything. +/// for book in &books { +/// println!("{}", book); +/// } +/// ``` +#[derive(Clone, Hash, PartialEq, Eq, Ord, PartialOrd)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct BTreeSet { + map: BTreeMap, +} + +/// An iterator over the items of a `BTreeSet`. +/// +/// This `struct` is created by the [`iter`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`iter`]: struct.BTreeSet.html#method.iter +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + iter: Keys<'a, T, ()>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Iter") + .field(&self.iter.clone()) + .finish() + } +} + +/// An owning iterator over the items of a `BTreeSet`. +/// +/// This `struct` is created by the [`into_iter`] method on [`BTreeSet`][`BTreeSet`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`into_iter`]: struct.BTreeSet.html#method.into_iter +#[stable(feature = "rust1", since = "1.0.0")] +#[derive(Debug)] +pub struct IntoIter { + iter: btree_map::IntoIter, +} + +/// An iterator over a sub-range of items in a `BTreeSet`. +/// +/// This `struct` is created by the [`range`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`range`]: struct.BTreeSet.html#method.range +#[derive(Debug)] +#[stable(feature = "btree_range", since = "1.17.0")] +pub struct Range<'a, T: 'a> { + iter: btree_map::Range<'a, T, ()>, +} + +/// A lazy iterator producing elements in the difference of `BTreeSet`s. +/// +/// This `struct` is created by the [`difference`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`difference`]: struct.BTreeSet.html#method.difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Difference<'a, T: 'a> { + a: Peekable>, + b: Peekable>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Difference<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Difference") + .field(&self.a) + .field(&self.b) + .finish() + } +} + +/// A lazy iterator producing elements in the symmetric difference of `BTreeSet`s. +/// +/// This `struct` is created by the [`symmetric_difference`] method on +/// [`BTreeSet`]. See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`symmetric_difference`]: struct.BTreeSet.html#method.symmetric_difference +#[stable(feature = "rust1", since = "1.0.0")] +pub struct SymmetricDifference<'a, T: 'a> { + a: Peekable>, + b: Peekable>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for SymmetricDifference<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("SymmetricDifference") + .field(&self.a) + .field(&self.b) + .finish() + } +} + +/// A lazy iterator producing elements in the intersection of `BTreeSet`s. +/// +/// This `struct` is created by the [`intersection`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`intersection`]: struct.BTreeSet.html#method.intersection +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Intersection<'a, T: 'a> { + a: Peekable>, + b: Peekable>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Intersection<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Intersection") + .field(&self.a) + .field(&self.b) + .finish() + } +} + +/// A lazy iterator producing elements in the union of `BTreeSet`s. +/// +/// This `struct` is created by the [`union`] method on [`BTreeSet`]. +/// See its documentation for more. +/// +/// [`BTreeSet`]: struct.BTreeSet.html +/// [`union`]: struct.BTreeSet.html#method.union +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Union<'a, T: 'a> { + a: Peekable>, + b: Peekable>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Union<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Union") + .field(&self.a) + .field(&self.b) + .finish() + } +} + +impl BTreeSet { + /// Makes a new `BTreeSet` with a reasonable choice of B. + /// + /// # Examples + /// + /// ``` + /// # #![allow(unused_mut)] + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet = BTreeSet::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> BTreeSet { + BTreeSet { map: BTreeMap::new() } + } + + /// Constructs a double-ended iterator over a sub-range of elements in the set. + /// The simplest way is to use the range syntax `min..max`, thus `range(min..max)` will + /// yield elements from min (inclusive) to max (exclusive). + /// The range may also be entered as `(Bound, Bound)`, so for example + /// `range((Excluded(4), Included(10)))` will yield a left-exclusive, right-inclusive + /// range from 4 to 10. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// use std::ops::Bound::Included; + /// + /// let mut set = BTreeSet::new(); + /// set.insert(3); + /// set.insert(5); + /// set.insert(8); + /// for &elem in set.range((Included(&4), Included(&8))) { + /// println!("{}", elem); + /// } + /// assert_eq!(Some(&5), set.range(4..).next()); + /// ``` + #[stable(feature = "btree_range", since = "1.17.0")] + pub fn range(&self, range: R) -> Range + where K: Ord, T: Borrow, R: RangeBounds + { + Range { iter: self.map.range(range) } + } + + /// Visits the values representing the difference, + /// i.e. the values that are in `self` but not in `other`, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let diff: Vec<_> = a.difference(&b).cloned().collect(); + /// assert_eq!(diff, [1]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn difference<'a>(&'a self, other: &'a BTreeSet) -> Difference<'a, T> { + Difference { + a: self.iter().peekable(), + b: other.iter().peekable(), + } + } + + /// Visits the values representing the symmetric difference, + /// i.e. the values that are in `self` or in `other` but not in both, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let sym_diff: Vec<_> = a.symmetric_difference(&b).cloned().collect(); + /// assert_eq!(sym_diff, [1, 3]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn symmetric_difference<'a>(&'a self, + other: &'a BTreeSet) + -> SymmetricDifference<'a, T> { + SymmetricDifference { + a: self.iter().peekable(), + b: other.iter().peekable(), + } + } + + /// Visits the values representing the intersection, + /// i.e. the values that are both in `self` and `other`, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// b.insert(3); + /// + /// let intersection: Vec<_> = a.intersection(&b).cloned().collect(); + /// assert_eq!(intersection, [2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn intersection<'a>(&'a self, other: &'a BTreeSet) -> Intersection<'a, T> { + Intersection { + a: self.iter().peekable(), + b: other.iter().peekable(), + } + } + + /// Visits the values representing the union, + /// i.e. all the values in `self` or `other`, without duplicates, + /// in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(2); + /// + /// let union: Vec<_> = a.union(&b).cloned().collect(); + /// assert_eq!(union, [1, 2]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn union<'a>(&'a self, other: &'a BTreeSet) -> Union<'a, T> { + Union { + a: self.iter().peekable(), + b: other.iter().peekable(), + } + } + + /// Clears the set, removing all values. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// v.insert(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + self.map.clear() + } + + /// Returns `true` if the set contains a value. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.contains(&1), true); + /// assert_eq!(set.contains(&4), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn contains(&self, value: &Q) -> bool + where T: Borrow, + Q: Ord + { + self.map.contains_key(value) + } + + /// Returns a reference to the value in the set, if any, that is equal to the given value. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.get(&2), Some(&2)); + /// assert_eq!(set.get(&4), None); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn get(&self, value: &Q) -> Option<&T> + where T: Borrow, + Q: Ord + { + Recover::get(&self.map, value) + } + + /// Returns `true` if `self` has no elements in common with `other`. + /// This is equivalent to checking for an empty intersection. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut b = BTreeSet::new(); + /// + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(4); + /// assert_eq!(a.is_disjoint(&b), true); + /// b.insert(1); + /// assert_eq!(a.is_disjoint(&b), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_disjoint(&self, other: &BTreeSet) -> bool { + self.intersection(other).next().is_none() + } + + /// Returns `true` if the set is a subset of another, + /// i.e. `other` contains at least all the values in `self`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let sup: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(2); + /// assert_eq!(set.is_subset(&sup), true); + /// set.insert(4); + /// assert_eq!(set.is_subset(&sup), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_subset(&self, other: &BTreeSet) -> bool { + // Stolen from TreeMap + let mut x = self.iter(); + let mut y = other.iter(); + let mut a = x.next(); + let mut b = y.next(); + while a.is_some() { + if b.is_none() { + return false; + } + + let a1 = a.unwrap(); + let b1 = b.unwrap(); + + match b1.cmp(a1) { + Less => (), + Greater => return false, + Equal => a = x.next(), + } + + b = y.next(); + } + true + } + + /// Returns `true` if the set is a superset of another, + /// i.e. `self` contains at least all the values in `other`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let sub: BTreeSet<_> = [1, 2].iter().cloned().collect(); + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(0); + /// set.insert(1); + /// assert_eq!(set.is_superset(&sub), false); + /// + /// set.insert(2); + /// assert_eq!(set.is_superset(&sub), true); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_superset(&self, other: &BTreeSet) -> bool { + other.is_subset(self) + } + + /// Adds a value to the set. + /// + /// If the set did not have this value present, `true` is returned. + /// + /// If the set did have this value present, `false` is returned, and the + /// entry is not updated. See the [module-level documentation] for more. + /// + /// [module-level documentation]: index.html#insert-and-complex-keys + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// assert_eq!(set.insert(2), true); + /// assert_eq!(set.insert(2), false); + /// assert_eq!(set.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn insert(&mut self, value: T) -> bool { + self.map.insert(value, ()).is_none() + } + + /// Adds a value to the set, replacing the existing value, if any, that is equal to the given + /// one. Returns the replaced value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// set.insert(Vec::::new()); + /// + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 0); + /// set.replace(Vec::with_capacity(10)); + /// assert_eq!(set.get(&[][..]).unwrap().capacity(), 10); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn replace(&mut self, value: T) -> Option { + Recover::replace(&mut self.map, value) + } + + /// Removes a value from the set. Returns `true` if the value was + /// present in the set. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set = BTreeSet::new(); + /// + /// set.insert(2); + /// assert_eq!(set.remove(&2), true); + /// assert_eq!(set.remove(&2), false); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, value: &Q) -> bool + where T: Borrow, + Q: Ord + { + self.map.remove(value).is_some() + } + + /// Removes and returns the value in the set, if any, that is equal to the given one. + /// + /// The value may be any borrowed form of the set's value type, + /// but the ordering on the borrowed form *must* match the + /// ordering on the value type. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut set: BTreeSet<_> = [1, 2, 3].iter().cloned().collect(); + /// assert_eq!(set.take(&2), Some(2)); + /// assert_eq!(set.take(&2), None); + /// ``` + #[stable(feature = "set_recovery", since = "1.9.0")] + pub fn take(&mut self, value: &Q) -> Option + where T: Borrow, + Q: Ord + { + Recover::take(&mut self.map, value) + } + + /// Moves all elements from `other` into `Self`, leaving `other` empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// a.insert(3); + /// + /// let mut b = BTreeSet::new(); + /// b.insert(3); + /// b.insert(4); + /// b.insert(5); + /// + /// a.append(&mut b); + /// + /// assert_eq!(a.len(), 5); + /// assert_eq!(b.len(), 0); + /// + /// assert!(a.contains(&1)); + /// assert!(a.contains(&2)); + /// assert!(a.contains(&3)); + /// assert!(a.contains(&4)); + /// assert!(a.contains(&5)); + /// ``` + #[stable(feature = "btree_append", since = "1.11.0")] + pub fn append(&mut self, other: &mut Self) { + self.map.append(&mut other.map); + } + + /// Splits the collection into two at the given key. Returns everything after the given key, + /// including the key. + /// + /// # Examples + /// + /// Basic usage: + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut a = BTreeSet::new(); + /// a.insert(1); + /// a.insert(2); + /// a.insert(3); + /// a.insert(17); + /// a.insert(41); + /// + /// let b = a.split_off(&3); + /// + /// assert_eq!(a.len(), 2); + /// assert_eq!(b.len(), 3); + /// + /// assert!(a.contains(&1)); + /// assert!(a.contains(&2)); + /// + /// assert!(b.contains(&3)); + /// assert!(b.contains(&17)); + /// assert!(b.contains(&41)); + /// ``` + #[stable(feature = "btree_split_off", since = "1.11.0")] + pub fn split_off(&mut self, key: &Q) -> Self where T: Borrow { + BTreeSet { map: self.map.split_off(key) } + } +} + +impl BTreeSet { + /// Gets an iterator that visits the values in the `BTreeSet` in ascending order. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [1, 2, 3].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); + /// ``` + /// + /// Values returned by the iterator are returned in ascending order: + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [3, 1, 2].iter().cloned().collect(); + /// let mut set_iter = set.iter(); + /// assert_eq!(set_iter.next(), Some(&1)); + /// assert_eq!(set_iter.next(), Some(&2)); + /// assert_eq!(set_iter.next(), Some(&3)); + /// assert_eq!(set_iter.next(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { iter: self.map.keys() } + } + + /// Returns the number of elements in the set. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// assert_eq!(v.len(), 0); + /// v.insert(1); + /// assert_eq!(v.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.map.len() + } + + /// Returns `true` if the set contains no elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let mut v = BTreeSet::new(); + /// assert!(v.is_empty()); + /// v.insert(1); + /// assert!(!v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.len() == 0 + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for BTreeSet { + fn from_iter>(iter: I) -> BTreeSet { + let mut set = BTreeSet::new(); + set.extend(iter); + set + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for BTreeSet { + type Item = T; + type IntoIter = IntoIter; + + /// Gets an iterator for moving out the `BTreeSet`'s contents. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let set: BTreeSet = [1, 2, 3, 4].iter().cloned().collect(); + /// + /// let v: Vec<_> = set.into_iter().collect(); + /// assert_eq!(v, [1, 2, 3, 4]); + /// ``` + fn into_iter(self) -> IntoIter { + IntoIter { iter: self.map.into_iter() } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a BTreeSet { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for BTreeSet { + #[inline] + fn extend>(&mut self, iter: Iter) { + for elem in iter { + self.insert(elem); + } + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Ord + Copy> Extend<&'a T> for BTreeSet { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for BTreeSet { + /// Makes an empty `BTreeSet` with a reasonable choice of B. + fn default() -> BTreeSet { + BTreeSet::new() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, 'b, T: Ord + Clone> Sub<&'b BTreeSet> for &'a BTreeSet { + type Output = BTreeSet; + + /// Returns the difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let result = &a - &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 2]); + /// ``` + fn sub(self, rhs: &BTreeSet) -> BTreeSet { + self.difference(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, 'b, T: Ord + Clone> BitXor<&'b BTreeSet> for &'a BTreeSet { + type Output = BTreeSet; + + /// Returns the symmetric difference of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); + /// + /// let result = &a ^ &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 4]); + /// ``` + fn bitxor(self, rhs: &BTreeSet) -> BTreeSet { + self.symmetric_difference(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, 'b, T: Ord + Clone> BitAnd<&'b BTreeSet> for &'a BTreeSet { + type Output = BTreeSet; + + /// Returns the intersection of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![2, 3, 4].into_iter().collect(); + /// + /// let result = &a & &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [2, 3]); + /// ``` + fn bitand(self, rhs: &BTreeSet) -> BTreeSet { + self.intersection(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, 'b, T: Ord + Clone> BitOr<&'b BTreeSet> for &'a BTreeSet { + type Output = BTreeSet; + + /// Returns the union of `self` and `rhs` as a new `BTreeSet`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::BTreeSet; + /// + /// let a: BTreeSet<_> = vec![1, 2, 3].into_iter().collect(); + /// let b: BTreeSet<_> = vec![3, 4, 5].into_iter().collect(); + /// + /// let result = &a | &b; + /// let result_vec: Vec<_> = result.into_iter().collect(); + /// assert_eq!(result_vec, [1, 2, 3, 4, 5]); + /// ``` + fn bitor(self, rhs: &BTreeSet) -> BTreeSet { + self.union(rhs).cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Debug for BTreeSet { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_set().entries(self.iter()).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Iter<'a, T> { + fn clone(&self) -> Iter<'a, T> { + Iter { iter: self.iter.clone() } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + self.iter.next() + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for Iter<'a, T> { + fn len(&self) -> usize { self.iter.len() } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for Iter<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + fn next(&mut self) -> Option { + self.iter.next().map(|(k, _)| k) + } + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|(k, _)| k) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn len(&self) -> usize { self.iter.len() } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, T> Clone for Range<'a, T> { + fn clone(&self) -> Range<'a, T> { + Range { iter: self.iter.clone() } + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, T> Iterator for Range<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + self.iter.next().map(|(k, _)| k) + } +} + +#[stable(feature = "btree_range", since = "1.17.0")] +impl<'a, T> DoubleEndedIterator for Range<'a, T> { + fn next_back(&mut self) -> Option<&'a T> { + self.iter.next_back().map(|(k, _)| k) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for Range<'a, T> {} + +/// Compare `x` and `y`, but return `short` if x is None and `long` if y is None +fn cmp_opt(x: Option<&T>, y: Option<&T>, short: Ordering, long: Ordering) -> Ordering { + match (x, y) { + (None, _) => short, + (_, None) => long, + (Some(x1), Some(y1)) => x1.cmp(y1), + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Difference<'a, T> { + fn clone(&self) -> Difference<'a, T> { + Difference { + a: self.a.clone(), + b: self.b.clone(), + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Difference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + loop { + match cmp_opt(self.a.peek(), self.b.peek(), Less, Less) { + Less => return self.a.next(), + Equal => { + self.a.next(); + self.b.next(); + } + Greater => { + self.b.next(); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + let a_len = self.a.len(); + let b_len = self.b.len(); + (a_len.saturating_sub(b_len), Some(a_len)) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: Ord> FusedIterator for Difference<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for SymmetricDifference<'a, T> { + fn clone(&self) -> SymmetricDifference<'a, T> { + SymmetricDifference { + a: self.a.clone(), + b: self.b.clone(), + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for SymmetricDifference<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + loop { + match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { + Less => return self.a.next(), + Equal => { + self.a.next(); + self.b.next(); + } + Greater => return self.b.next(), + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.a.len() + self.b.len())) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: Ord> FusedIterator for SymmetricDifference<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Intersection<'a, T> { + fn clone(&self) -> Intersection<'a, T> { + Intersection { + a: self.a.clone(), + b: self.b.clone(), + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Intersection<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + loop { + match Ord::cmp(self.a.peek()?, self.b.peek()?) { + Less => { + self.a.next(); + } + Equal => { + self.b.next(); + return self.a.next(); + } + Greater => { + self.b.next(); + } + } + } + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(min(self.a.len(), self.b.len()))) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: Ord> FusedIterator for Intersection<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Union<'a, T> { + fn clone(&self) -> Union<'a, T> { + Union { + a: self.a.clone(), + b: self.b.clone(), + } + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T: Ord> Iterator for Union<'a, T> { + type Item = &'a T; + + fn next(&mut self) -> Option<&'a T> { + match cmp_opt(self.a.peek(), self.b.peek(), Greater, Less) { + Less => self.a.next(), + Equal => { + self.b.next(); + self.a.next() + } + Greater => self.b.next(), + } + } + + fn size_hint(&self) -> (usize, Option) { + let a_len = self.a.len(); + let b_len = self.b.len(); + (max(a_len, b_len), Some(a_len + b_len)) + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: Ord> FusedIterator for Union<'a, T> {} diff --git a/src/liballoc/collections/linked_list.rs b/src/liballoc/collections/linked_list.rs new file mode 100644 index 00000000000..9844de9a57d --- /dev/null +++ b/src/liballoc/collections/linked_list.rs @@ -0,0 +1,1486 @@ +// Copyright 2012-2015 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A doubly-linked list with owned nodes. +//! +//! The `LinkedList` allows pushing and popping elements at either end +//! in constant time. +//! +//! Almost always it is better to use `Vec` or [`VecDeque`] instead of +//! [`LinkedList`]. In general, array-based containers are faster, +//! more memory efficient and make better use of CPU cache. +//! +//! [`LinkedList`]: ../linked_list/struct.LinkedList.html +//! [`VecDeque`]: ../vec_deque/struct.VecDeque.html + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::fmt; +use core::hash::{Hasher, Hash}; +use core::iter::{FromIterator, FusedIterator}; +use core::marker::PhantomData; +use core::mem; +use core::ptr::NonNull; + +use boxed::Box; +use super::SpecExtend; + +/// A doubly-linked list with owned nodes. +/// +/// The `LinkedList` allows pushing and popping elements at either end +/// in constant time. +/// +/// Almost always it is better to use `Vec` or `VecDeque` instead of +/// `LinkedList`. In general, array-based containers are faster, +/// more memory efficient and make better use of CPU cache. +#[stable(feature = "rust1", since = "1.0.0")] +pub struct LinkedList { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData>>, +} + +struct Node { + next: Option>>, + prev: Option>>, + element: T, +} + +/// An iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`iter`] method on [`LinkedList`]. See its +/// documentation for more. +/// +/// [`iter`]: struct.LinkedList.html#method.iter +/// [`LinkedList`]: struct.LinkedList.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + head: Option>>, + tail: Option>>, + len: usize, + marker: PhantomData<&'a Node>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Iter") + .field(&self.len) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Iter<'a, T> { + fn clone(&self) -> Self { + Iter { ..*self } + } +} + +/// A mutable iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`LinkedList`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: struct.LinkedList.html#method.iter_mut +/// [`LinkedList`]: struct.LinkedList.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + list: &'a mut LinkedList, + head: Option>>, + tail: Option>>, + len: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&self.list) + .field(&self.len) + .finish() + } +} + +/// An owning iterator over the elements of a `LinkedList`. +/// +/// This `struct` is created by the [`into_iter`] method on [`LinkedList`][`LinkedList`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: struct.LinkedList.html#method.into_iter +/// [`LinkedList`]: struct.LinkedList.html +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + list: LinkedList, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("IntoIter") + .field(&self.list) + .finish() + } +} + +impl Node { + fn new(element: T) -> Self { + Node { + next: None, + prev: None, + element, + } + } + + fn into_element(self: Box) -> T { + self.element + } +} + +// private methods +impl LinkedList { + /// Adds the given node to the front of the list. + #[inline] + fn push_front_node(&mut self, mut node: Box>) { + unsafe { + node.next = self.head; + node.prev = None; + let node = Some(Box::into_raw_non_null(node)); + + match self.head { + None => self.tail = node, + Some(mut head) => head.as_mut().prev = node, + } + + self.head = node; + self.len += 1; + } + } + + /// Removes and returns the node at the front of the list. + #[inline] + fn pop_front_node(&mut self) -> Option>> { + self.head.map(|node| unsafe { + let node = Box::from_raw(node.as_ptr()); + self.head = node.next; + + match self.head { + None => self.tail = None, + Some(mut head) => head.as_mut().prev = None, + } + + self.len -= 1; + node + }) + } + + /// Adds the given node to the back of the list. + #[inline] + fn push_back_node(&mut self, mut node: Box>) { + unsafe { + node.next = None; + node.prev = self.tail; + let node = Some(Box::into_raw_non_null(node)); + + match self.tail { + None => self.head = node, + Some(mut tail) => tail.as_mut().next = node, + } + + self.tail = node; + self.len += 1; + } + } + + /// Removes and returns the node at the back of the list. + #[inline] + fn pop_back_node(&mut self) -> Option>> { + self.tail.map(|node| unsafe { + let node = Box::from_raw(node.as_ptr()); + self.tail = node.prev; + + match self.tail { + None => self.head = None, + Some(mut tail) => tail.as_mut().next = None, + } + + self.len -= 1; + node + }) + } + + /// Unlinks the specified node from the current list. + /// + /// Warning: this will not check that the provided node belongs to the current list. + #[inline] + unsafe fn unlink_node(&mut self, mut node: NonNull>) { + let node = node.as_mut(); + + match node.prev { + Some(mut prev) => prev.as_mut().next = node.next.clone(), + // this node is the head node + None => self.head = node.next.clone(), + }; + + match node.next { + Some(mut next) => next.as_mut().prev = node.prev.clone(), + // this node is the tail node + None => self.tail = node.prev.clone(), + }; + + self.len -= 1; + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for LinkedList { + /// Creates an empty `LinkedList`. + #[inline] + fn default() -> Self { + Self::new() + } +} + +impl LinkedList { + /// Creates an empty `LinkedList`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let list: LinkedList = LinkedList::new(); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> Self { + LinkedList { + head: None, + tail: None, + len: 0, + marker: PhantomData, + } + } + + /// Moves all elements from `other` to the end of the list. + /// + /// This reuses all the nodes from `other` and moves them into `self`. After + /// this operation, `other` becomes empty. + /// + /// This operation should compute in O(1) time and O(1) memory. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list1 = LinkedList::new(); + /// list1.push_back('a'); + /// + /// let mut list2 = LinkedList::new(); + /// list2.push_back('b'); + /// list2.push_back('c'); + /// + /// list1.append(&mut list2); + /// + /// let mut iter = list1.iter(); + /// assert_eq!(iter.next(), Some(&'a')); + /// assert_eq!(iter.next(), Some(&'b')); + /// assert_eq!(iter.next(), Some(&'c')); + /// assert!(iter.next().is_none()); + /// + /// assert!(list2.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn append(&mut self, other: &mut Self) { + match self.tail { + None => mem::swap(self, other), + Some(mut tail) => { + if let Some(mut other_head) = other.head.take() { + unsafe { + tail.as_mut().next = Some(other_head); + other_head.as_mut().prev = Some(tail); + } + + self.tail = other.tail.take(); + self.len += mem::replace(&mut other.len, 0); + } + } + } + } + + /// Provides a forward iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&0)); + /// assert_eq!(iter.next(), Some(&1)); + /// assert_eq!(iter.next(), Some(&2)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { + head: self.head, + tail: self.tail, + len: self.len, + marker: PhantomData, + } + } + + /// Provides a forward iterator with mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// for element in list.iter_mut() { + /// *element += 10; + /// } + /// + /// let mut iter = list.iter(); + /// assert_eq!(iter.next(), Some(&10)); + /// assert_eq!(iter.next(), Some(&11)); + /// assert_eq!(iter.next(), Some(&12)); + /// assert_eq!(iter.next(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + head: self.head, + tail: self.tail, + len: self.len, + list: self, + } + } + + /// Returns `true` if the `LinkedList` is empty. + /// + /// This operation should compute in O(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert!(dl.is_empty()); + /// + /// dl.push_front("foo"); + /// assert!(!dl.is_empty()); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.head.is_none() + } + + /// Returns the length of the `LinkedList`. + /// + /// This operation should compute in O(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.len(), 1); + /// + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// + /// dl.push_back(3); + /// assert_eq!(dl.len(), 3); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + self.len + } + + /// Removes all elements from the `LinkedList`. + /// + /// This operation should compute in O(n) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// dl.push_front(1); + /// assert_eq!(dl.len(), 2); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// dl.clear(); + /// assert_eq!(dl.len(), 0); + /// assert_eq!(dl.front(), None); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn clear(&mut self) { + *self = Self::new(); + } + + /// Returns `true` if the `LinkedList` contains an element equal to the + /// given value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList = LinkedList::new(); + /// + /// list.push_back(0); + /// list.push_back(1); + /// list.push_back(2); + /// + /// assert_eq!(list.contains(&0), true); + /// assert_eq!(list.contains(&10), false); + /// ``` + #[stable(feature = "linked_list_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where T: PartialEq + { + self.iter().any(|e| e == x) + } + + /// Provides a reference to the front element, or `None` if the list is + /// empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front(&self) -> Option<&T> { + unsafe { + self.head.as_ref().map(|node| &node.as_ref().element) + } + } + + /// Provides a mutable reference to the front element, or `None` if the list + /// is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.front(), None); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front(), Some(&1)); + /// + /// match dl.front_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.front(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + unsafe { + self.head.as_mut().map(|node| &mut node.as_mut().element) + } + } + + /// Provides a reference to the back element, or `None` if the list is + /// empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + unsafe { + self.tail.as_ref().map(|node| &node.as_ref().element) + } + } + + /// Provides a mutable reference to the back element, or `None` if the list + /// is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// assert_eq!(dl.back(), None); + /// + /// dl.push_back(1); + /// assert_eq!(dl.back(), Some(&1)); + /// + /// match dl.back_mut() { + /// None => {}, + /// Some(x) => *x = 5, + /// } + /// assert_eq!(dl.back(), Some(&5)); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + unsafe { + self.tail.as_mut().map(|node| &mut node.as_mut().element) + } + } + + /// Adds an element first in the list. + /// + /// This operation should compute in O(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut dl = LinkedList::new(); + /// + /// dl.push_front(2); + /// assert_eq!(dl.front().unwrap(), &2); + /// + /// dl.push_front(1); + /// assert_eq!(dl.front().unwrap(), &1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, elt: T) { + self.push_front_node(box Node::new(elt)); + } + + /// Removes the first element and returns it, or `None` if the list is + /// empty. + /// + /// This operation should compute in O(1) time. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_front(), None); + /// + /// d.push_front(1); + /// d.push_front(3); + /// assert_eq!(d.pop_front(), Some(3)); + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + self.pop_front_node().map(Node::into_element) + } + + /// Appends an element to the back of a list + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(3, *d.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_back(&mut self, elt: T) { + self.push_back_node(box Node::new(elt)); + } + + /// Removes the last element from a list and returns it, or `None` if + /// it is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// assert_eq!(d.pop_back(), None); + /// d.push_back(1); + /// d.push_back(3); + /// assert_eq!(d.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + self.pop_back_node().map(Node::into_element) + } + + /// Splits the list into two at the given index. Returns everything after the given index, + /// including the index. + /// + /// This operation should compute in O(n) time. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::LinkedList; + /// + /// let mut d = LinkedList::new(); + /// + /// d.push_front(1); + /// d.push_front(2); + /// d.push_front(3); + /// + /// let mut splitted = d.split_off(2); + /// + /// assert_eq!(splitted.pop_front(), Some(1)); + /// assert_eq!(splitted.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn split_off(&mut self, at: usize) -> LinkedList { + let len = self.len(); + assert!(at <= len, "Cannot split off at a nonexistent index"); + if at == 0 { + return mem::replace(self, Self::new()); + } else if at == len { + return Self::new(); + } + + // Below, we iterate towards the `i-1`th node, either from the start or the end, + // depending on which would be faster. + let split_node = if at - 1 <= len - 1 - (at - 1) { + let mut iter = self.iter_mut(); + // instead of skipping using .skip() (which creates a new struct), + // we skip manually so we can access the head field without + // depending on implementation details of Skip + for _ in 0..at - 1 { + iter.next(); + } + iter.head + } else { + // better off starting from the end + let mut iter = self.iter_mut(); + for _ in 0..len - 1 - (at - 1) { + iter.next_back(); + } + iter.tail + }; + + // The split node is the new tail node of the first part and owns + // the head of the second part. + let second_part_head; + + unsafe { + second_part_head = split_node.unwrap().as_mut().next.take(); + if let Some(mut head) = second_part_head { + head.as_mut().prev = None; + } + } + + let second_part = LinkedList { + head: second_part_head, + tail: self.tail, + len: len - at, + marker: PhantomData, + }; + + // Fix the tail ptr of the first part + self.tail = split_node; + self.len = at; + + second_part + } + + /// Creates an iterator which uses a closure to determine if an element should be removed. + /// + /// If the closure returns true, then the element is removed and yielded. + /// If the closure returns false, the element will remain in the list and will not be yielded + /// by the iterator. + /// + /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of + /// whether you choose to keep or remove it. + /// + /// # Examples + /// + /// Splitting a list into evens and odds, reusing the original list: + /// + /// ``` + /// #![feature(drain_filter)] + /// use std::collections::LinkedList; + /// + /// let mut numbers: LinkedList = LinkedList::new(); + /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); + /// + /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); + /// let odds = numbers; + /// + /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); + /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); + /// ``` + #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] + pub fn drain_filter(&mut self, filter: F) -> DrainFilter + where F: FnMut(&mut T) -> bool + { + // avoid borrow issues. + let it = self.head; + let old_len = self.len; + + DrainFilter { + list: self, + it: it, + pred: filter, + idx: 0, + old_len: old_len, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T> Drop for LinkedList { + fn drop(&mut self) { + while let Some(_) = self.pop_front_node() {} + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.head = node.next; + &node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &*node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for Iter<'a, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for Iter<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.head.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.head = node.next; + &mut node.element + }) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.len, Some(self.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.len == 0 { + None + } else { + self.tail.map(|node| unsafe { + // Need an unbound lifetime to get 'a + let node = &mut *node.as_ptr(); + self.len -= 1; + self.tail = node.prev; + &mut node.element + }) + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for IterMut<'a, T> {} + +impl<'a, T> IterMut<'a, T> { + /// Inserts the given element just after the element most recently returned by `.next()`. + /// The inserted element does not appear in the iteration. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_extras)] + /// + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList<_> = vec![1, 3, 4].into_iter().collect(); + /// + /// { + /// let mut it = list.iter_mut(); + /// assert_eq!(it.next().unwrap(), &1); + /// // insert `2` after `1` + /// it.insert_next(2); + /// } + /// { + /// let vec: Vec<_> = list.into_iter().collect(); + /// assert_eq!(vec, [1, 2, 3, 4]); + /// } + /// ``` + #[inline] + #[unstable(feature = "linked_list_extras", + reason = "this is probably better handled by a cursor type -- we'll see", + issue = "27794")] + pub fn insert_next(&mut self, element: T) { + match self.head { + None => self.list.push_back(element), + Some(mut head) => unsafe { + let mut prev = match head.as_ref().prev { + None => return self.list.push_front(element), + Some(prev) => prev, + }; + + let node = Some(Box::into_raw_non_null(box Node { + next: Some(head), + prev: Some(prev), + element, + })); + + prev.as_mut().next = node; + head.as_mut().prev = node; + + self.list.len += 1; + }, + } + } + + /// Provides a reference to the next element, without changing the iterator. + /// + /// # Examples + /// + /// ``` + /// #![feature(linked_list_extras)] + /// + /// use std::collections::LinkedList; + /// + /// let mut list: LinkedList<_> = vec![1, 2, 3].into_iter().collect(); + /// + /// let mut it = list.iter_mut(); + /// assert_eq!(it.next().unwrap(), &1); + /// assert_eq!(it.peek_next().unwrap(), &2); + /// // We just peeked at 2, so it was not consumed from the iterator. + /// assert_eq!(it.next().unwrap(), &2); + /// ``` + #[inline] + #[unstable(feature = "linked_list_extras", + reason = "this is probably better handled by a cursor type -- we'll see", + issue = "27794")] + pub fn peek_next(&mut self) -> Option<&mut T> { + if self.len == 0 { + None + } else { + unsafe { + self.head.as_mut().map(|node| &mut node.as_mut().element) + } + } + } +} + +/// An iterator produced by calling `drain_filter` on LinkedList. +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +pub struct DrainFilter<'a, T: 'a, F: 'a> + where F: FnMut(&mut T) -> bool, +{ + list: &'a mut LinkedList, + it: Option>>, + pred: F, + idx: usize, + old_len: usize, +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl<'a, T, F> Iterator for DrainFilter<'a, T, F> + where F: FnMut(&mut T) -> bool, +{ + type Item = T; + + fn next(&mut self) -> Option { + while let Some(mut node) = self.it { + unsafe { + self.it = node.as_ref().next; + self.idx += 1; + + if (self.pred)(&mut node.as_mut().element) { + self.list.unlink_node(node); + return Some(Box::from_raw(node.as_ptr()).element); + } + } + } + + None + } + + fn size_hint(&self) -> (usize, Option) { + (0, Some(self.old_len - self.idx)) + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl<'a, T, F> Drop for DrainFilter<'a, T, F> + where F: FnMut(&mut T) -> bool, +{ + fn drop(&mut self) { + self.for_each(drop); + } +} + +#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] +impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for DrainFilter<'a, T, F> + where F: FnMut(&mut T) -> bool +{ + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("DrainFilter") + .field(&self.list) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.list.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + (self.list.len, Some(self.list.len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.list.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter {} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for LinkedList { + fn from_iter>(iter: I) -> Self { + let mut list = Self::new(); + list.extend(iter); + list + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for LinkedList { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the list into an iterator yielding elements by value. + #[inline] + fn into_iter(self) -> IntoIter { + IntoIter { list: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a LinkedList { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a mut LinkedList { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for LinkedList { + fn extend>(&mut self, iter: I) { + >::spec_extend(self, iter); + } +} + +impl SpecExtend for LinkedList { + default fn spec_extend(&mut self, iter: I) { + for elt in iter { + self.push_back(elt); + } + } +} + +impl SpecExtend> for LinkedList { + fn spec_extend(&mut self, ref mut other: LinkedList) { + self.append(other); + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy> Extend<&'a T> for LinkedList { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for LinkedList { + fn eq(&self, other: &Self) -> bool { + self.len() == other.len() && self.iter().eq(other) + } + + fn ne(&self, other: &Self) -> bool { + self.len() != other.len() || self.iter().ne(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for LinkedList { + fn partial_cmp(&self, other: &Self) -> Option { + self.iter().partial_cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for LinkedList { + #[inline] + fn cmp(&self, other: &Self) -> Ordering { + self.iter().cmp(other) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for LinkedList { + fn clone(&self) -> Self { + self.iter().cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for LinkedList { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for LinkedList { + fn hash(&self, state: &mut H) { + self.len().hash(state); + for elt in self { + elt.hash(state); + } + } +} + +// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. +#[allow(dead_code)] +fn assert_covariance() { + fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { + x + } + fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { + x + } + fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { + x + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for LinkedList {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<'a, T: Sync> Send for Iter<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} + +#[cfg(test)] +mod tests { + use std::thread; + use std::vec::Vec; + + use rand::{thread_rng, Rng}; + + use super::{LinkedList, Node}; + + #[cfg(test)] + fn list_from(v: &[T]) -> LinkedList { + v.iter().cloned().collect() + } + + pub fn check_links(list: &LinkedList) { + unsafe { + let mut len = 0; + let mut last_ptr: Option<&Node> = None; + let mut node_ptr: &Node; + match list.head { + None => { + // tail node should also be None. + assert!(list.tail.is_none()); + assert_eq!(0, list.len); + return; + } + Some(node) => node_ptr = &*node.as_ptr(), + } + loop { + match (last_ptr, node_ptr.prev) { + (None, None) => {} + (None, _) => panic!("prev link for head"), + (Some(p), Some(pptr)) => { + assert_eq!(p as *const Node, pptr.as_ptr() as *const Node); + } + _ => panic!("prev link is none, not good"), + } + match node_ptr.next { + Some(next) => { + last_ptr = Some(node_ptr); + node_ptr = &*next.as_ptr(); + len += 1; + } + None => { + len += 1; + break; + } + } + } + + // verify that the tail node points to the last node. + let tail = list.tail.as_ref().expect("some tail node").as_ref(); + assert_eq!(tail as *const Node, node_ptr as *const Node); + // check that len matches interior links. + assert_eq!(len, list.len); + } + } + + #[test] + fn test_append() { + // Empty to empty + { + let mut m = LinkedList::::new(); + let mut n = LinkedList::new(); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 0); + assert_eq!(n.len(), 0); + } + // Non-empty to empty + { + let mut m = LinkedList::new(); + let mut n = LinkedList::new(); + n.push_back(2); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 1); + assert_eq!(m.pop_back(), Some(2)); + assert_eq!(n.len(), 0); + check_links(&m); + } + // Empty to non-empty + { + let mut m = LinkedList::new(); + let mut n = LinkedList::new(); + m.push_back(2); + m.append(&mut n); + check_links(&m); + assert_eq!(m.len(), 1); + assert_eq!(m.pop_back(), Some(2)); + check_links(&m); + } + + // Non-empty to non-empty + let v = vec![1, 2, 3, 4, 5]; + let u = vec![9, 8, 1, 2, 3, 4, 5]; + let mut m = list_from(&v); + let mut n = list_from(&u); + m.append(&mut n); + check_links(&m); + let mut sum = v; + sum.extend_from_slice(&u); + assert_eq!(sum.len(), m.len()); + for elt in sum { + assert_eq!(m.pop_front(), Some(elt)) + } + assert_eq!(n.len(), 0); + // let's make sure it's working properly, since we + // did some direct changes to private members + n.push_back(3); + assert_eq!(n.len(), 1); + assert_eq!(n.pop_front(), Some(3)); + check_links(&n); + } + + #[test] + fn test_insert_prev() { + let mut m = list_from(&[0, 2, 4, 6, 8]); + let len = m.len(); + { + let mut it = m.iter_mut(); + it.insert_next(-2); + loop { + match it.next() { + None => break, + Some(elt) => { + it.insert_next(*elt + 1); + match it.peek_next() { + Some(x) => assert_eq!(*x, *elt + 2), + None => assert_eq!(8, *elt), + } + } + } + } + it.insert_next(0); + it.insert_next(1); + } + check_links(&m); + assert_eq!(m.len(), 3 + len * 2); + assert_eq!(m.into_iter().collect::>(), + [-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn test_send() { + let n = list_from(&[1, 2, 3]); + thread::spawn(move || { + check_links(&n); + let a: &[_] = &[&1, &2, &3]; + assert_eq!(a, &*n.iter().collect::>()); + }) + .join() + .ok() + .unwrap(); + } + + #[test] + fn test_fuzz() { + for _ in 0..25 { + fuzz_test(3); + fuzz_test(16); + fuzz_test(189); + } + } + + #[test] + fn test_26021() { + // There was a bug in split_off that failed to null out the RHS's head's prev ptr. + // This caused the RHS's dtor to walk up into the LHS at drop and delete all of + // its nodes. + // + // https://github.com/rust-lang/rust/issues/26021 + let mut v1 = LinkedList::new(); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + let _ = v1.split_off(3); // Dropping this now should not cause laundry consumption + assert_eq!(v1.len(), 3); + + assert_eq!(v1.iter().len(), 3); + assert_eq!(v1.iter().collect::>().len(), 3); + } + + #[test] + fn test_split_off() { + let mut v1 = LinkedList::new(); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + v1.push_front(1); + + // test all splits + for ix in 0..1 + v1.len() { + let mut a = v1.clone(); + let b = a.split_off(ix); + check_links(&a); + check_links(&b); + a.extend(b); + assert_eq!(v1, a); + } + } + + #[cfg(test)] + fn fuzz_test(sz: i32) { + let mut m: LinkedList<_> = LinkedList::new(); + let mut v = vec![]; + for i in 0..sz { + check_links(&m); + let r: u8 = thread_rng().next_u32() as u8; + match r % 6 { + 0 => { + m.pop_back(); + v.pop(); + } + 1 => { + if !v.is_empty() { + m.pop_front(); + v.remove(0); + } + } + 2 | 4 => { + m.push_front(-i); + v.insert(0, -i); + } + 3 | 5 | _ => { + m.push_back(i); + v.push(i); + } + } + } + + check_links(&m); + + let mut i = 0; + for (a, &b) in m.into_iter().zip(&v) { + i += 1; + assert_eq!(a, b); + } + assert_eq!(i, v.len()); + } + + #[test] + fn drain_filter_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|v| *v < 4).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3]); + assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); + } + + #[test] + fn drain_to_empty_test() { + let mut m: LinkedList = LinkedList::new(); + m.extend(&[1, 2, 3, 4, 5, 6]); + let deleted = m.drain_filter(|_| true).collect::>(); + + check_links(&m); + + assert_eq!(deleted, &[1, 2, 3, 4, 5, 6]); + assert_eq!(m.into_iter().collect::>(), &[]); + } +} diff --git a/src/liballoc/collections/mod.rs b/src/liballoc/collections/mod.rs new file mode 100644 index 00000000000..35c816a1ceb --- /dev/null +++ b/src/liballoc/collections/mod.rs @@ -0,0 +1,59 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! Collection types. + +#![stable(feature = "rust1", since = "1.0.0")] + +pub mod binary_heap; +mod btree; +pub mod linked_list; +pub mod vec_deque; + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod btree_map { + //! A map based on a B-Tree. + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::btree::map::*; +} + +#[stable(feature = "rust1", since = "1.0.0")] +pub mod btree_set { + //! A set based on a B-Tree. + #[stable(feature = "rust1", since = "1.0.0")] + pub use super::btree::set::*; +} + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use self::binary_heap::BinaryHeap; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use self::btree_map::BTreeMap; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use self::btree_set::BTreeSet; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use self::linked_list::LinkedList; + +#[stable(feature = "rust1", since = "1.0.0")] +#[doc(no_inline)] +pub use self::vec_deque::VecDeque; + +/// An intermediate trait for specialization of `Extend`. +#[doc(hidden)] +trait SpecExtend { + /// Extends `self` with the contents of the given iterator. + fn spec_extend(&mut self, iter: I); +} diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs new file mode 100644 index 00000000000..4753d36415c --- /dev/null +++ b/src/liballoc/collections/vec_deque.rs @@ -0,0 +1,2970 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! A double-ended queue implemented with a growable ring buffer. +//! +//! This queue has `O(1)` amortized inserts and removals from both ends of the +//! container. It also has `O(1)` indexing like a vector. The contained elements +//! are not required to be copyable, and the queue will be sendable if the +//! contained type is sendable. + +#![stable(feature = "rust1", since = "1.0.0")] + +use core::cmp::Ordering; +use core::fmt; +use core::iter::{repeat, FromIterator, FusedIterator}; +use core::mem; +use core::ops::Bound::{Excluded, Included, Unbounded}; +use core::ops::{Index, IndexMut, RangeBounds}; +use core::ptr; +use core::ptr::NonNull; +use core::slice; + +use core::hash::{Hash, Hasher}; +use core::cmp; + +use alloc::CollectionAllocErr; +use raw_vec::RawVec; +use vec::Vec; + +const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 +const MINIMUM_CAPACITY: usize = 1; // 2 - 1 +#[cfg(target_pointer_width = "32")] +const MAXIMUM_ZST_CAPACITY: usize = 1 << (32 - 1); // Largest possible power of two +#[cfg(target_pointer_width = "64")] +const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of two + +/// A double-ended queue implemented with a growable ring buffer. +/// +/// The "default" usage of this type as a queue is to use [`push_back`] to add to +/// the queue, and [`pop_front`] to remove from the queue. [`extend`] and [`append`] +/// push onto the back in this manner, and iterating over `VecDeque` goes front +/// to back. +/// +/// [`push_back`]: #method.push_back +/// [`pop_front`]: #method.pop_front +/// [`extend`]: #method.extend +/// [`append`]: #method.append +#[stable(feature = "rust1", since = "1.0.0")] +pub struct VecDeque { + // tail and head are pointers into the buffer. Tail always points + // to the first element that could be read, Head always points + // to where data should be written. + // If tail == head the buffer is empty. The length of the ringbuffer + // is defined as the distance between the two. + tail: usize, + head: usize, + buf: RawVec, +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for VecDeque { + fn clone(&self) -> VecDeque { + self.iter().cloned().collect() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T> Drop for VecDeque { + fn drop(&mut self) { + let (front, back) = self.as_mut_slices(); + unsafe { + // use drop for [T] + ptr::drop_in_place(front); + ptr::drop_in_place(back); + } + // RawVec handles deallocation + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for VecDeque { + /// Creates an empty `VecDeque`. + #[inline] + fn default() -> VecDeque { + VecDeque::new() + } +} + +impl VecDeque { + /// Marginally more convenient + #[inline] + fn ptr(&self) -> *mut T { + self.buf.ptr() + } + + /// Marginally more convenient + #[inline] + fn cap(&self) -> usize { + if mem::size_of::() == 0 { + // For zero sized types, we are always at maximum capacity + MAXIMUM_ZST_CAPACITY + } else { + self.buf.cap() + } + } + + /// Turn ptr into a slice + #[inline] + unsafe fn buffer_as_slice(&self) -> &[T] { + slice::from_raw_parts(self.ptr(), self.cap()) + } + + /// Turn ptr into a mut slice + #[inline] + unsafe fn buffer_as_mut_slice(&mut self) -> &mut [T] { + slice::from_raw_parts_mut(self.ptr(), self.cap()) + } + + /// Moves an element out of the buffer + #[inline] + unsafe fn buffer_read(&mut self, off: usize) -> T { + ptr::read(self.ptr().offset(off as isize)) + } + + /// Writes an element into the buffer, moving it. + #[inline] + unsafe fn buffer_write(&mut self, off: usize, value: T) { + ptr::write(self.ptr().offset(off as isize), value); + } + + /// Returns `true` if and only if the buffer is at full capacity. + #[inline] + fn is_full(&self) -> bool { + self.cap() - self.len() == 1 + } + + /// Returns the index in the underlying buffer for a given logical element + /// index. + #[inline] + fn wrap_index(&self, idx: usize) -> usize { + wrap_index(idx, self.cap()) + } + + /// Returns the index in the underlying buffer for a given logical element + /// index + addend. + #[inline] + fn wrap_add(&self, idx: usize, addend: usize) -> usize { + wrap_index(idx.wrapping_add(addend), self.cap()) + } + + /// Returns the index in the underlying buffer for a given logical element + /// index - subtrahend. + #[inline] + fn wrap_sub(&self, idx: usize, subtrahend: usize) -> usize { + wrap_index(idx.wrapping_sub(subtrahend), self.cap()) + } + + /// Copies a contiguous block of memory len long from src to dst + #[inline] + unsafe fn copy(&self, dst: usize, src: usize, len: usize) { + debug_assert!(dst + len <= self.cap(), + "cpy dst={} src={} len={} cap={}", + dst, + src, + len, + self.cap()); + debug_assert!(src + len <= self.cap(), + "cpy dst={} src={} len={} cap={}", + dst, + src, + len, + self.cap()); + ptr::copy(self.ptr().offset(src as isize), + self.ptr().offset(dst as isize), + len); + } + + /// Copies a contiguous block of memory len long from src to dst + #[inline] + unsafe fn copy_nonoverlapping(&self, dst: usize, src: usize, len: usize) { + debug_assert!(dst + len <= self.cap(), + "cno dst={} src={} len={} cap={}", + dst, + src, + len, + self.cap()); + debug_assert!(src + len <= self.cap(), + "cno dst={} src={} len={} cap={}", + dst, + src, + len, + self.cap()); + ptr::copy_nonoverlapping(self.ptr().offset(src as isize), + self.ptr().offset(dst as isize), + len); + } + + /// Copies a potentially wrapping block of memory len long from src to dest. + /// (abs(dst - src) + len) must be no larger than cap() (There must be at + /// most one continuous overlapping region between src and dest). + unsafe fn wrap_copy(&self, dst: usize, src: usize, len: usize) { + #[allow(dead_code)] + fn diff(a: usize, b: usize) -> usize { + if a <= b { b - a } else { a - b } + } + debug_assert!(cmp::min(diff(dst, src), self.cap() - diff(dst, src)) + len <= self.cap(), + "wrc dst={} src={} len={} cap={}", + dst, + src, + len, + self.cap()); + + if src == dst || len == 0 { + return; + } + + let dst_after_src = self.wrap_sub(dst, src) < len; + + let src_pre_wrap_len = self.cap() - src; + let dst_pre_wrap_len = self.cap() - dst; + let src_wraps = src_pre_wrap_len < len; + let dst_wraps = dst_pre_wrap_len < len; + + match (dst_after_src, src_wraps, dst_wraps) { + (_, false, false) => { + // src doesn't wrap, dst doesn't wrap + // + // S . . . + // 1 [_ _ A A B B C C _] + // 2 [_ _ A A A A B B _] + // D . . . + // + self.copy(dst, src, len); + } + (false, false, true) => { + // dst before src, src doesn't wrap, dst wraps + // + // S . . . + // 1 [A A B B _ _ _ C C] + // 2 [A A B B _ _ _ A A] + // 3 [B B B B _ _ _ A A] + // . . D . + // + self.copy(dst, src, dst_pre_wrap_len); + self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); + } + (true, false, true) => { + // src before dst, src doesn't wrap, dst wraps + // + // S . . . + // 1 [C C _ _ _ A A B B] + // 2 [B B _ _ _ A A B B] + // 3 [B B _ _ _ A A A A] + // . . D . + // + self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); + self.copy(dst, src, dst_pre_wrap_len); + } + (false, true, false) => { + // dst before src, src wraps, dst doesn't wrap + // + // . . S . + // 1 [C C _ _ _ A A B B] + // 2 [C C _ _ _ B B B B] + // 3 [C C _ _ _ B B C C] + // D . . . + // + self.copy(dst, src, src_pre_wrap_len); + self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); + } + (true, true, false) => { + // src before dst, src wraps, dst doesn't wrap + // + // . . S . + // 1 [A A B B _ _ _ C C] + // 2 [A A A A _ _ _ C C] + // 3 [C C A A _ _ _ C C] + // D . . . + // + self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); + self.copy(dst, src, src_pre_wrap_len); + } + (false, true, true) => { + // dst before src, src wraps, dst wraps + // + // . . . S . + // 1 [A B C D _ E F G H] + // 2 [A B C D _ E G H H] + // 3 [A B C D _ E G H A] + // 4 [B C C D _ E G H A] + // . . D . . + // + debug_assert!(dst_pre_wrap_len > src_pre_wrap_len); + let delta = dst_pre_wrap_len - src_pre_wrap_len; + self.copy(dst, src, src_pre_wrap_len); + self.copy(dst + src_pre_wrap_len, 0, delta); + self.copy(0, delta, len - dst_pre_wrap_len); + } + (true, true, true) => { + // src before dst, src wraps, dst wraps + // + // . . S . . + // 1 [A B C D _ E F G H] + // 2 [A A B D _ E F G H] + // 3 [H A B D _ E F G H] + // 4 [H A B D _ E F F G] + // . . . D . + // + debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); + let delta = src_pre_wrap_len - dst_pre_wrap_len; + self.copy(delta, 0, len - src_pre_wrap_len); + self.copy(0, self.cap() - delta, delta); + self.copy(dst, src, dst_pre_wrap_len); + } + } + } + + /// Frobs the head and tail sections around to handle the fact that we + /// just reallocated. Unsafe because it trusts old_cap. + #[inline] + unsafe fn handle_cap_increase(&mut self, old_cap: usize) { + let new_cap = self.cap(); + + // Move the shortest contiguous section of the ring buffer + // T H + // [o o o o o o o . ] + // T H + // A [o o o o o o o . . . . . . . . . ] + // H T + // [o o . o o o o o ] + // T H + // B [. . . o o o o o o o . . . . . . ] + // H T + // [o o o o o . o o ] + // H T + // C [o o o o o . . . . . . . . . o o ] + + if self.tail <= self.head { + // A + // Nop + } else if self.head < old_cap - self.tail { + // B + self.copy_nonoverlapping(old_cap, 0, self.head); + self.head += old_cap; + debug_assert!(self.head > self.tail); + } else { + // C + let new_tail = new_cap - (old_cap - self.tail); + self.copy_nonoverlapping(new_tail, self.tail, old_cap - self.tail); + self.tail = new_tail; + debug_assert!(self.head < self.tail); + } + debug_assert!(self.head < self.cap()); + debug_assert!(self.tail < self.cap()); + debug_assert!(self.cap().count_ones() == 1); + } +} + +impl VecDeque { + /// Creates an empty `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let vector: VecDeque = VecDeque::new(); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new() -> VecDeque { + VecDeque::with_capacity(INITIAL_CAPACITY) + } + + /// Creates an empty `VecDeque` with space for at least `n` elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let vector: VecDeque = VecDeque::with_capacity(10); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn with_capacity(n: usize) -> VecDeque { + // +1 since the ringbuffer always leaves one space empty + let cap = cmp::max(n + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); + assert!(cap > n, "capacity overflow"); + + VecDeque { + tail: 0, + head: 0, + buf: RawVec::with_capacity(cap), + } + } + + /// Retrieves an element in the `VecDeque` by index. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(3); + /// buf.push_back(4); + /// buf.push_back(5); + /// assert_eq!(buf.get(1), Some(&4)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get(&self, index: usize) -> Option<&T> { + if index < self.len() { + let idx = self.wrap_add(self.tail, index); + unsafe { Some(&*self.ptr().offset(idx as isize)) } + } else { + None + } + } + + /// Retrieves an element in the `VecDeque` mutably by index. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(3); + /// buf.push_back(4); + /// buf.push_back(5); + /// if let Some(elem) = buf.get_mut(1) { + /// *elem = 7; + /// } + /// + /// assert_eq!(buf[1], 7); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { + if index < self.len() { + let idx = self.wrap_add(self.tail, index); + unsafe { Some(&mut *self.ptr().offset(idx as isize)) } + } else { + None + } + } + + /// Swaps elements at indices `i` and `j`. + /// + /// `i` and `j` may be equal. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Panics + /// + /// Panics if either index is out of bounds. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(3); + /// buf.push_back(4); + /// buf.push_back(5); + /// assert_eq!(buf, [3, 4, 5]); + /// buf.swap(0, 2); + /// assert_eq!(buf, [5, 4, 3]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn swap(&mut self, i: usize, j: usize) { + assert!(i < self.len()); + assert!(j < self.len()); + let ri = self.wrap_add(self.tail, i); + let rj = self.wrap_add(self.tail, j); + unsafe { + ptr::swap(self.ptr().offset(ri as isize), + self.ptr().offset(rj as isize)) + } + } + + /// Returns the number of elements the `VecDeque` can hold without + /// reallocating. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let buf: VecDeque = VecDeque::with_capacity(10); + /// assert!(buf.capacity() >= 10); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn capacity(&self) -> usize { + self.cap() - 1 + } + + /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the + /// given `VecDeque`. Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it requests. Therefore + /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future + /// insertions are expected. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque = vec![1].into_iter().collect(); + /// buf.reserve_exact(10); + /// assert!(buf.capacity() >= 11); + /// ``` + /// + /// [`reserve`]: #method.reserve + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve_exact(&mut self, additional: usize) { + self.reserve(additional); + } + + /// Reserves capacity for at least `additional` more elements to be inserted in the given + /// `VecDeque`. The collection may reserve more space to avoid frequent reallocations. + /// + /// # Panics + /// + /// Panics if the new capacity overflows `usize`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque = vec![1].into_iter().collect(); + /// buf.reserve(10); + /// assert!(buf.capacity() >= 11); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn reserve(&mut self, additional: usize) { + let old_cap = self.cap(); + let used_cap = self.len() + 1; + let new_cap = used_cap.checked_add(additional) + .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) + .expect("capacity overflow"); + + if new_cap > old_cap { + self.buf.reserve_exact(used_cap, new_cap - used_cap); + unsafe { + self.handle_cap_increase(old_cap); + } + } + } + + /// Tries to reserves the minimum capacity for exactly `additional` more elements to + /// be inserted in the given `VecDeque`. After calling `reserve_exact`, + /// capacity will be greater than or equal to `self.len() + additional`. + /// Does nothing if the capacity is already sufficient. + /// + /// Note that the allocator may give the collection more space than it + /// requests. Therefore capacity can not be relied upon to be precisely + /// minimal. Prefer `reserve` if future insertions are expected. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// use std::collections::VecDeque; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = VecDeque::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve_exact(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + self.try_reserve(additional) + } + + /// Tries to reserve capacity for at least `additional` more elements to be inserted + /// in the given `VecDeque`. The collection may reserve more space to avoid + /// frequent reallocations. After calling `reserve`, capacity will be + /// greater than or equal to `self.len() + additional`. Does nothing if + /// capacity is already sufficient. + /// + /// # Errors + /// + /// If the capacity overflows, or the allocator reports a failure, then an error + /// is returned. + /// + /// # Examples + /// + /// ``` + /// #![feature(try_reserve)] + /// use std::collections::CollectionAllocErr; + /// use std::collections::VecDeque; + /// + /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { + /// let mut output = VecDeque::new(); + /// + /// // Pre-reserve the memory, exiting if we can't + /// output.try_reserve(data.len())?; + /// + /// // Now we know this can't OOM in the middle of our complex work + /// output.extend(data.iter().map(|&val| { + /// val * 2 + 5 // very complicated + /// })); + /// + /// Ok(output) + /// } + /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); + /// ``` + #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] + pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { + let old_cap = self.cap(); + let used_cap = self.len() + 1; + let new_cap = used_cap.checked_add(additional) + .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) + .ok_or(CollectionAllocErr::CapacityOverflow)?; + + if new_cap > old_cap { + self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?; + unsafe { + self.handle_cap_increase(old_cap); + } + } + Ok(()) + } + + /// Shrinks the capacity of the `VecDeque` as much as possible. + /// + /// It will drop down as close as possible to the length but the allocator may still inform the + /// `VecDeque` that there is space for a few more elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::with_capacity(15); + /// buf.extend(0..4); + /// assert_eq!(buf.capacity(), 15); + /// buf.shrink_to_fit(); + /// assert!(buf.capacity() >= 4); + /// ``` + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn shrink_to_fit(&mut self) { + self.shrink_to(0); + } + + /// Shrinks the capacity of the `VecDeque` with a lower bound. + /// + /// The capacity will remain at least as large as both the length + /// and the supplied value. + /// + /// Panics if the current capacity is smaller than the supplied + /// minimum capacity. + /// + /// # Examples + /// + /// ``` + /// #![feature(shrink_to)] + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::with_capacity(15); + /// buf.extend(0..4); + /// assert_eq!(buf.capacity(), 15); + /// buf.shrink_to(6); + /// assert!(buf.capacity() >= 6); + /// buf.shrink_to(0); + /// assert!(buf.capacity() >= 4); + /// ``` + #[unstable(feature = "shrink_to", reason = "new API", issue="0")] + pub fn shrink_to(&mut self, min_capacity: usize) { + assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); + + // +1 since the ringbuffer always leaves one space empty + // len + 1 can't overflow for an existing, well-formed ringbuffer. + let target_cap = cmp::max( + cmp::max(min_capacity, self.len()) + 1, + MINIMUM_CAPACITY + 1 + ).next_power_of_two(); + + if target_cap < self.cap() { + // There are three cases of interest: + // All elements are out of desired bounds + // Elements are contiguous, and head is out of desired bounds + // Elements are discontiguous, and tail is out of desired bounds + // + // At all other times, element positions are unaffected. + // + // Indicates that elements at the head should be moved. + let head_outside = self.head == 0 || self.head >= target_cap; + // Move elements from out of desired bounds (positions after target_cap) + if self.tail >= target_cap && head_outside { + // T H + // [. . . . . . . . o o o o o o o . ] + // T H + // [o o o o o o o . ] + unsafe { + self.copy_nonoverlapping(0, self.tail, self.len()); + } + self.head = self.len(); + self.tail = 0; + } else if self.tail != 0 && self.tail < target_cap && head_outside { + // T H + // [. . . o o o o o o o . . . . . . ] + // H T + // [o o . o o o o o ] + let len = self.wrap_sub(self.head, target_cap); + unsafe { + self.copy_nonoverlapping(0, target_cap, len); + } + self.head = len; + debug_assert!(self.head < self.tail); + } else if self.tail >= target_cap { + // H T + // [o o o o o . . . . . . . . . o o ] + // H T + // [o o o o o . o o ] + debug_assert!(self.wrap_sub(self.head, 1) < target_cap); + let len = self.cap() - self.tail; + let new_tail = target_cap - len; + unsafe { + self.copy_nonoverlapping(new_tail, self.tail, len); + } + self.tail = new_tail; + debug_assert!(self.head < self.tail); + } + + self.buf.shrink_to_fit(target_cap); + + debug_assert!(self.head < self.cap()); + debug_assert!(self.tail < self.cap()); + debug_assert!(self.cap().count_ones() == 1); + } + } + + /// Shortens the `VecDeque`, dropping excess elements from the back. + /// + /// If `len` is greater than the `VecDeque`'s current length, this has no + /// effect. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(5); + /// buf.push_back(10); + /// buf.push_back(15); + /// assert_eq!(buf, [5, 10, 15]); + /// buf.truncate(1); + /// assert_eq!(buf, [5]); + /// ``` + #[stable(feature = "deque_extras", since = "1.16.0")] + pub fn truncate(&mut self, len: usize) { + for _ in len..self.len() { + self.pop_back(); + } + } + + /// Returns a front-to-back iterator. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(5); + /// buf.push_back(3); + /// buf.push_back(4); + /// let b: &[_] = &[&5, &3, &4]; + /// let c: Vec<&i32> = buf.iter().collect(); + /// assert_eq!(&c[..], b); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter(&self) -> Iter { + Iter { + tail: self.tail, + head: self.head, + ring: unsafe { self.buffer_as_slice() }, + } + } + + /// Returns a front-to-back iterator that returns mutable references. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(5); + /// buf.push_back(3); + /// buf.push_back(4); + /// for num in buf.iter_mut() { + /// *num = *num - 2; + /// } + /// let b: &[_] = &[&mut 3, &mut 1, &mut 2]; + /// assert_eq!(&buf.iter_mut().collect::>()[..], b); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn iter_mut(&mut self) -> IterMut { + IterMut { + tail: self.tail, + head: self.head, + ring: unsafe { self.buffer_as_mut_slice() }, + } + } + + /// Returns a pair of slices which contain, in order, the contents of the + /// `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut vector = VecDeque::new(); + /// + /// vector.push_back(0); + /// vector.push_back(1); + /// vector.push_back(2); + /// + /// assert_eq!(vector.as_slices(), (&[0, 1, 2][..], &[][..])); + /// + /// vector.push_front(10); + /// vector.push_front(9); + /// + /// assert_eq!(vector.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); + /// ``` + #[inline] + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn as_slices(&self) -> (&[T], &[T]) { + unsafe { + let buf = self.buffer_as_slice(); + RingSlices::ring_slices(buf, self.head, self.tail) + } + } + + /// Returns a pair of slices which contain, in order, the contents of the + /// `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut vector = VecDeque::new(); + /// + /// vector.push_back(0); + /// vector.push_back(1); + /// + /// vector.push_front(10); + /// vector.push_front(9); + /// + /// vector.as_mut_slices().0[0] = 42; + /// vector.as_mut_slices().1[0] = 24; + /// assert_eq!(vector.as_slices(), (&[42, 10][..], &[24, 1][..])); + /// ``` + #[inline] + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { + unsafe { + let head = self.head; + let tail = self.tail; + let buf = self.buffer_as_mut_slice(); + RingSlices::ring_slices(buf, head, tail) + } + } + + /// Returns the number of elements in the `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut v = VecDeque::new(); + /// assert_eq!(v.len(), 0); + /// v.push_back(1); + /// assert_eq!(v.len(), 1); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn len(&self) -> usize { + count(self.tail, self.head, self.cap()) + } + + /// Returns `true` if the `VecDeque` is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut v = VecDeque::new(); + /// assert!(v.is_empty()); + /// v.push_front(1); + /// assert!(!v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn is_empty(&self) -> bool { + self.tail == self.head + } + + /// Create a draining iterator that removes the specified range in the + /// `VecDeque` and yields the removed items. + /// + /// Note 1: The element range is removed even if the iterator is not + /// consumed until the end. + /// + /// Note 2: It is unspecified how many elements are removed from the deque, + /// if the `Drain` value is not dropped, but the borrow it holds expires + /// (eg. due to mem::forget). + /// + /// # Panics + /// + /// Panics if the starting point is greater than the end point or if + /// the end point is greater than the length of the vector. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); + /// let drained = v.drain(2..).collect::>(); + /// assert_eq!(drained, [3]); + /// assert_eq!(v, [1, 2]); + /// + /// // A full range clears all contents + /// v.drain(..); + /// assert!(v.is_empty()); + /// ``` + #[inline] + #[stable(feature = "drain", since = "1.6.0")] + pub fn drain(&mut self, range: R) -> Drain + where R: RangeBounds + { + // Memory safety + // + // When the Drain is first created, the source deque is shortened to + // make sure no uninitialized or moved-from elements are accessible at + // all if the Drain's destructor never gets to run. + // + // Drain will ptr::read out the values to remove. + // When finished, the remaining data will be copied back to cover the hole, + // and the head/tail values will be restored correctly. + // + let len = self.len(); + let start = match range.start_bound() { + Included(&n) => n, + Excluded(&n) => n + 1, + Unbounded => 0, + }; + let end = match range.end_bound() { + Included(&n) => n + 1, + Excluded(&n) => n, + Unbounded => len, + }; + assert!(start <= end, "drain lower bound was too large"); + assert!(end <= len, "drain upper bound was too large"); + + // The deque's elements are parted into three segments: + // * self.tail -> drain_tail + // * drain_tail -> drain_head + // * drain_head -> self.head + // + // T = self.tail; H = self.head; t = drain_tail; h = drain_head + // + // We store drain_tail as self.head, and drain_head and self.head as + // after_tail and after_head respectively on the Drain. This also + // truncates the effective array such that if the Drain is leaked, we + // have forgotten about the potentially moved values after the start of + // the drain. + // + // T t h H + // [. . . o o x x o o . . .] + // + let drain_tail = self.wrap_add(self.tail, start); + let drain_head = self.wrap_add(self.tail, end); + let head = self.head; + + // "forget" about the values after the start of the drain until after + // the drain is complete and the Drain destructor is run. + self.head = drain_tail; + + Drain { + deque: NonNull::from(&mut *self), + after_tail: drain_head, + after_head: head, + iter: Iter { + tail: drain_tail, + head: drain_head, + ring: unsafe { self.buffer_as_mut_slice() }, + }, + } + } + + /// Clears the `VecDeque`, removing all values. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut v = VecDeque::new(); + /// v.push_back(1); + /// v.clear(); + /// assert!(v.is_empty()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + #[inline] + pub fn clear(&mut self) { + self.drain(..); + } + + /// Returns `true` if the `VecDeque` contains an element equal to the + /// given value. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut vector: VecDeque = VecDeque::new(); + /// + /// vector.push_back(0); + /// vector.push_back(1); + /// + /// assert_eq!(vector.contains(&1), true); + /// assert_eq!(vector.contains(&10), false); + /// ``` + #[stable(feature = "vec_deque_contains", since = "1.12.0")] + pub fn contains(&self, x: &T) -> bool + where T: PartialEq + { + let (a, b) = self.as_slices(); + a.contains(x) || b.contains(x) + } + + /// Provides a reference to the front element, or `None` if the `VecDeque` is + /// empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// assert_eq!(d.front(), None); + /// + /// d.push_back(1); + /// d.push_back(2); + /// assert_eq!(d.front(), Some(&1)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front(&self) -> Option<&T> { + if !self.is_empty() { + Some(&self[0]) + } else { + None + } + } + + /// Provides a mutable reference to the front element, or `None` if the + /// `VecDeque` is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// assert_eq!(d.front_mut(), None); + /// + /// d.push_back(1); + /// d.push_back(2); + /// match d.front_mut() { + /// Some(x) => *x = 9, + /// None => (), + /// } + /// assert_eq!(d.front(), Some(&9)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn front_mut(&mut self) -> Option<&mut T> { + if !self.is_empty() { + Some(&mut self[0]) + } else { + None + } + } + + /// Provides a reference to the back element, or `None` if the `VecDeque` is + /// empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// assert_eq!(d.back(), None); + /// + /// d.push_back(1); + /// d.push_back(2); + /// assert_eq!(d.back(), Some(&2)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back(&self) -> Option<&T> { + if !self.is_empty() { + Some(&self[self.len() - 1]) + } else { + None + } + } + + /// Provides a mutable reference to the back element, or `None` if the + /// `VecDeque` is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// assert_eq!(d.back(), None); + /// + /// d.push_back(1); + /// d.push_back(2); + /// match d.back_mut() { + /// Some(x) => *x = 9, + /// None => (), + /// } + /// assert_eq!(d.back(), Some(&9)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn back_mut(&mut self) -> Option<&mut T> { + let len = self.len(); + if !self.is_empty() { + Some(&mut self[len - 1]) + } else { + None + } + } + + /// Removes the first element and returns it, or `None` if the `VecDeque` is + /// empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// d.push_back(1); + /// d.push_back(2); + /// + /// assert_eq!(d.pop_front(), Some(1)); + /// assert_eq!(d.pop_front(), Some(2)); + /// assert_eq!(d.pop_front(), None); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_front(&mut self) -> Option { + if self.is_empty() { + None + } else { + let tail = self.tail; + self.tail = self.wrap_add(self.tail, 1); + unsafe { Some(self.buffer_read(tail)) } + } + } + + /// Prepends an element to the `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut d = VecDeque::new(); + /// d.push_front(1); + /// d.push_front(2); + /// assert_eq!(d.front(), Some(&2)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_front(&mut self, value: T) { + self.grow_if_necessary(); + + self.tail = self.wrap_sub(self.tail, 1); + let tail = self.tail; + unsafe { + self.buffer_write(tail, value); + } + } + + /// Appends an element to the back of the `VecDeque`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(1); + /// buf.push_back(3); + /// assert_eq!(3, *buf.back().unwrap()); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn push_back(&mut self, value: T) { + self.grow_if_necessary(); + + let head = self.head; + self.head = self.wrap_add(self.head, 1); + unsafe { self.buffer_write(head, value) } + } + + /// Removes the last element from the `VecDeque` and returns it, or `None` if + /// it is empty. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// assert_eq!(buf.pop_back(), None); + /// buf.push_back(1); + /// buf.push_back(3); + /// assert_eq!(buf.pop_back(), Some(3)); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn pop_back(&mut self) -> Option { + if self.is_empty() { + None + } else { + self.head = self.wrap_sub(self.head, 1); + let head = self.head; + unsafe { Some(self.buffer_read(head)) } + } + } + + #[inline] + fn is_contiguous(&self) -> bool { + self.tail <= self.head + } + + /// Removes an element from anywhere in the `VecDeque` and returns it, replacing it with the + /// last element. + /// + /// This does not preserve ordering, but is O(1). + /// + /// Returns `None` if `index` is out of bounds. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// assert_eq!(buf.swap_remove_back(0), None); + /// buf.push_back(1); + /// buf.push_back(2); + /// buf.push_back(3); + /// assert_eq!(buf, [1, 2, 3]); + /// + /// assert_eq!(buf.swap_remove_back(0), Some(1)); + /// assert_eq!(buf, [3, 2]); + /// ``` + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn swap_remove_back(&mut self, index: usize) -> Option { + let length = self.len(); + if length > 0 && index < length - 1 { + self.swap(index, length - 1); + } else if index >= length { + return None; + } + self.pop_back() + } + + /// Removes an element from anywhere in the `VecDeque` and returns it, + /// replacing it with the first element. + /// + /// This does not preserve ordering, but is O(1). + /// + /// Returns `None` if `index` is out of bounds. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// assert_eq!(buf.swap_remove_front(0), None); + /// buf.push_back(1); + /// buf.push_back(2); + /// buf.push_back(3); + /// assert_eq!(buf, [1, 2, 3]); + /// + /// assert_eq!(buf.swap_remove_front(2), Some(3)); + /// assert_eq!(buf, [2, 1]); + /// ``` + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn swap_remove_front(&mut self, index: usize) -> Option { + let length = self.len(); + if length > 0 && index < length && index != 0 { + self.swap(index, 0); + } else if index >= length { + return None; + } + self.pop_front() + } + + /// Inserts an element at `index` within the `VecDeque`, shifting all elements with indices + /// greater than or equal to `index` towards the back. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Panics + /// + /// Panics if `index` is greater than `VecDeque`'s length + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut vec_deque = VecDeque::new(); + /// vec_deque.push_back('a'); + /// vec_deque.push_back('b'); + /// vec_deque.push_back('c'); + /// assert_eq!(vec_deque, &['a', 'b', 'c']); + /// + /// vec_deque.insert(1, 'd'); + /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']); + /// ``` + #[stable(feature = "deque_extras_15", since = "1.5.0")] + pub fn insert(&mut self, index: usize, value: T) { + assert!(index <= self.len(), "index out of bounds"); + self.grow_if_necessary(); + + // Move the least number of elements in the ring buffer and insert + // the given object + // + // At most len/2 - 1 elements will be moved. O(min(n, n-i)) + // + // There are three main cases: + // Elements are contiguous + // - special case when tail is 0 + // Elements are discontiguous and the insert is in the tail section + // Elements are discontiguous and the insert is in the head section + // + // For each of those there are two more cases: + // Insert is closer to tail + // Insert is closer to head + // + // Key: H - self.head + // T - self.tail + // o - Valid element + // I - Insertion element + // A - The element that should be after the insertion point + // M - Indicates element was moved + + let idx = self.wrap_add(self.tail, index); + + let distance_to_tail = index; + let distance_to_head = self.len() - index; + + let contiguous = self.is_contiguous(); + + match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { + (true, true, _) if index == 0 => { + // push_front + // + // T + // I H + // [A o o o o o o . . . . . . . . .] + // + // H T + // [A o o o o o o o . . . . . I] + // + + self.tail = self.wrap_sub(self.tail, 1); + } + (true, true, _) => { + unsafe { + // contiguous, insert closer to tail: + // + // T I H + // [. . . o o A o o o o . . . . . .] + // + // T H + // [. . o o I A o o o o . . . . . .] + // M M + // + // contiguous, insert closer to tail and tail is 0: + // + // + // T I H + // [o o A o o o o . . . . . . . . .] + // + // H T + // [o I A o o o o o . . . . . . . o] + // M M + + let new_tail = self.wrap_sub(self.tail, 1); + + self.copy(new_tail, self.tail, 1); + // Already moved the tail, so we only copy `index - 1` elements. + self.copy(self.tail, self.tail + 1, index - 1); + + self.tail = new_tail; + } + } + (true, false, _) => { + unsafe { + // contiguous, insert closer to head: + // + // T I H + // [. . . o o o o A o o . . . . . .] + // + // T H + // [. . . o o o o I A o o . . . . .] + // M M M + + self.copy(idx + 1, idx, self.head - idx); + self.head = self.wrap_add(self.head, 1); + } + } + (false, true, true) => { + unsafe { + // discontiguous, insert closer to tail, tail section: + // + // H T I + // [o o o o o o . . . . . o o A o o] + // + // H T + // [o o o o o o . . . . o o I A o o] + // M M + + self.copy(self.tail - 1, self.tail, index); + self.tail -= 1; + } + } + (false, false, true) => { + unsafe { + // discontiguous, insert closer to head, tail section: + // + // H T I + // [o o . . . . . . . o o o o o A o] + // + // H T + // [o o o . . . . . . o o o o o I A] + // M M M M + + // copy elements up to new head + self.copy(1, 0, self.head); + + // copy last element into empty spot at bottom of buffer + self.copy(0, self.cap() - 1, 1); + + // move elements from idx to end forward not including ^ element + self.copy(idx + 1, idx, self.cap() - 1 - idx); + + self.head += 1; + } + } + (false, true, false) if idx == 0 => { + unsafe { + // discontiguous, insert is closer to tail, head section, + // and is at index zero in the internal buffer: + // + // I H T + // [A o o o o o o o o o . . . o o o] + // + // H T + // [A o o o o o o o o o . . o o o I] + // M M M + + // copy elements up to new tail + self.copy(self.tail - 1, self.tail, self.cap() - self.tail); + + // copy last element into empty spot at bottom of buffer + self.copy(self.cap() - 1, 0, 1); + + self.tail -= 1; + } + } + (false, true, false) => { + unsafe { + // discontiguous, insert closer to tail, head section: + // + // I H T + // [o o o A o o o o o o . . . o o o] + // + // H T + // [o o I A o o o o o o . . o o o o] + // M M M M M M + + // copy elements up to new tail + self.copy(self.tail - 1, self.tail, self.cap() - self.tail); + + // copy last element into empty spot at bottom of buffer + self.copy(self.cap() - 1, 0, 1); + + // move elements from idx-1 to end forward not including ^ element + self.copy(0, 1, idx - 1); + + self.tail -= 1; + } + } + (false, false, false) => { + unsafe { + // discontiguous, insert closer to head, head section: + // + // I H T + // [o o o o A o o . . . . . . o o o] + // + // H T + // [o o o o I A o o . . . . . o o o] + // M M M + + self.copy(idx + 1, idx, self.head - idx); + self.head += 1; + } + } + } + + // tail might've been changed so we need to recalculate + let new_idx = self.wrap_add(self.tail, index); + unsafe { + self.buffer_write(new_idx, value); + } + } + + /// Removes and returns the element at `index` from the `VecDeque`. + /// Whichever end is closer to the removal point will be moved to make + /// room, and all the affected elements will be moved to new positions. + /// Returns `None` if `index` is out of bounds. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(1); + /// buf.push_back(2); + /// buf.push_back(3); + /// assert_eq!(buf, [1, 2, 3]); + /// + /// assert_eq!(buf.remove(1), Some(2)); + /// assert_eq!(buf, [1, 3]); + /// ``` + #[stable(feature = "rust1", since = "1.0.0")] + pub fn remove(&mut self, index: usize) -> Option { + if self.is_empty() || self.len() <= index { + return None; + } + + // There are three main cases: + // Elements are contiguous + // Elements are discontiguous and the removal is in the tail section + // Elements are discontiguous and the removal is in the head section + // - special case when elements are technically contiguous, + // but self.head = 0 + // + // For each of those there are two more cases: + // Insert is closer to tail + // Insert is closer to head + // + // Key: H - self.head + // T - self.tail + // o - Valid element + // x - Element marked for removal + // R - Indicates element that is being removed + // M - Indicates element was moved + + let idx = self.wrap_add(self.tail, index); + + let elem = unsafe { Some(self.buffer_read(idx)) }; + + let distance_to_tail = index; + let distance_to_head = self.len() - index; + + let contiguous = self.is_contiguous(); + + match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { + (true, true, _) => { + unsafe { + // contiguous, remove closer to tail: + // + // T R H + // [. . . o o x o o o o . . . . . .] + // + // T H + // [. . . . o o o o o o . . . . . .] + // M M + + self.copy(self.tail + 1, self.tail, index); + self.tail += 1; + } + } + (true, false, _) => { + unsafe { + // contiguous, remove closer to head: + // + // T R H + // [. . . o o o o x o o . . . . . .] + // + // T H + // [. . . o o o o o o . . . . . . .] + // M M + + self.copy(idx, idx + 1, self.head - idx - 1); + self.head -= 1; + } + } + (false, true, true) => { + unsafe { + // discontiguous, remove closer to tail, tail section: + // + // H T R + // [o o o o o o . . . . . o o x o o] + // + // H T + // [o o o o o o . . . . . . o o o o] + // M M + + self.copy(self.tail + 1, self.tail, index); + self.tail = self.wrap_add(self.tail, 1); + } + } + (false, false, false) => { + unsafe { + // discontiguous, remove closer to head, head section: + // + // R H T + // [o o o o x o o . . . . . . o o o] + // + // H T + // [o o o o o o . . . . . . . o o o] + // M M + + self.copy(idx, idx + 1, self.head - idx - 1); + self.head -= 1; + } + } + (false, false, true) => { + unsafe { + // discontiguous, remove closer to head, tail section: + // + // H T R + // [o o o . . . . . . o o o o o x o] + // + // H T + // [o o . . . . . . . o o o o o o o] + // M M M M + // + // or quasi-discontiguous, remove next to head, tail section: + // + // H T R + // [. . . . . . . . . o o o o o x o] + // + // T H + // [. . . . . . . . . o o o o o o .] + // M + + // draw in elements in the tail section + self.copy(idx, idx + 1, self.cap() - idx - 1); + + // Prevents underflow. + if self.head != 0 { + // copy first element into empty spot + self.copy(self.cap() - 1, 0, 1); + + // move elements in the head section backwards + self.copy(0, 1, self.head - 1); + } + + self.head = self.wrap_sub(self.head, 1); + } + } + (false, true, false) => { + unsafe { + // discontiguous, remove closer to tail, head section: + // + // R H T + // [o o x o o o o o o o . . . o o o] + // + // H T + // [o o o o o o o o o o . . . . o o] + // M M M M M + + // draw in elements up to idx + self.copy(1, 0, idx); + + // copy last element into empty spot + self.copy(0, self.cap() - 1, 1); + + // move elements from tail to end forward, excluding the last one + self.copy(self.tail + 1, self.tail, self.cap() - self.tail - 1); + + self.tail = self.wrap_add(self.tail, 1); + } + } + } + + return elem; + } + + /// Splits the `VecDeque` into two at the given index. + /// + /// Returns a newly allocated `VecDeque`. `self` contains elements `[0, at)`, + /// and the returned `VecDeque` contains elements `[at, len)`. + /// + /// Note that the capacity of `self` does not change. + /// + /// Element at index 0 is the front of the queue. + /// + /// # Panics + /// + /// Panics if `at > len`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque<_> = vec![1,2,3].into_iter().collect(); + /// let buf2 = buf.split_off(1); + /// assert_eq!(buf, [1]); + /// assert_eq!(buf2, [2, 3]); + /// ``` + #[inline] + #[stable(feature = "split_off", since = "1.4.0")] + pub fn split_off(&mut self, at: usize) -> Self { + let len = self.len(); + assert!(at <= len, "`at` out of bounds"); + + let other_len = len - at; + let mut other = VecDeque::with_capacity(other_len); + + unsafe { + let (first_half, second_half) = self.as_slices(); + + let first_len = first_half.len(); + let second_len = second_half.len(); + if at < first_len { + // `at` lies in the first half. + let amount_in_first = first_len - at; + + ptr::copy_nonoverlapping(first_half.as_ptr().offset(at as isize), + other.ptr(), + amount_in_first); + + // just take all of the second half. + ptr::copy_nonoverlapping(second_half.as_ptr(), + other.ptr().offset(amount_in_first as isize), + second_len); + } else { + // `at` lies in the second half, need to factor in the elements we skipped + // in the first half. + let offset = at - first_len; + let amount_in_second = second_len - offset; + ptr::copy_nonoverlapping(second_half.as_ptr().offset(offset as isize), + other.ptr(), + amount_in_second); + } + } + + // Cleanup where the ends of the buffers are + self.head = self.wrap_sub(self.head, other_len); + other.head = other.wrap_index(other_len); + + other + } + + /// Moves all the elements of `other` into `Self`, leaving `other` empty. + /// + /// # Panics + /// + /// Panics if the new number of elements in self overflows a `usize`. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf: VecDeque<_> = vec![1, 2].into_iter().collect(); + /// let mut buf2: VecDeque<_> = vec![3, 4].into_iter().collect(); + /// buf.append(&mut buf2); + /// assert_eq!(buf, [1, 2, 3, 4]); + /// assert_eq!(buf2, []); + /// ``` + #[inline] + #[stable(feature = "append", since = "1.4.0")] + pub fn append(&mut self, other: &mut Self) { + // naive impl + self.extend(other.drain(..)); + } + + /// Retains only the elements specified by the predicate. + /// + /// In other words, remove all elements `e` such that `f(&e)` returns false. + /// This method operates in place and preserves the order of the retained + /// elements. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.extend(1..5); + /// buf.retain(|&x| x%2 == 0); + /// assert_eq!(buf, [2, 4]); + /// ``` + #[stable(feature = "vec_deque_retain", since = "1.4.0")] + pub fn retain(&mut self, mut f: F) + where F: FnMut(&T) -> bool + { + let len = self.len(); + let mut del = 0; + for i in 0..len { + if !f(&self[i]) { + del += 1; + } else if del > 0 { + self.swap(i - del, i); + } + } + if del > 0 { + self.truncate(len - del); + } + } + + // This may panic or abort + #[inline] + fn grow_if_necessary(&mut self) { + if self.is_full() { + let old_cap = self.cap(); + self.buf.double(); + unsafe { + self.handle_cap_increase(old_cap); + } + debug_assert!(!self.is_full()); + } + } +} + +impl VecDeque { + /// Modifies the `VecDeque` in-place so that `len()` is equal to new_len, + /// either by removing excess elements from the back or by appending clones of `value` + /// to the back. + /// + /// # Examples + /// + /// ``` + /// use std::collections::VecDeque; + /// + /// let mut buf = VecDeque::new(); + /// buf.push_back(5); + /// buf.push_back(10); + /// buf.push_back(15); + /// assert_eq!(buf, [5, 10, 15]); + /// + /// buf.resize(2, 0); + /// assert_eq!(buf, [5, 10]); + /// + /// buf.resize(5, 20); + /// assert_eq!(buf, [5, 10, 20, 20, 20]); + /// ``` + #[stable(feature = "deque_extras", since = "1.16.0")] + pub fn resize(&mut self, new_len: usize, value: T) { + let len = self.len(); + + if new_len > len { + self.extend(repeat(value).take(new_len - len)) + } else { + self.truncate(new_len); + } + } +} + +/// Returns the index in the underlying buffer for a given logical element index. +#[inline] +fn wrap_index(index: usize, size: usize) -> usize { + // size is always a power of 2 + debug_assert!(size.is_power_of_two()); + index & (size - 1) +} + +/// Returns the two slices that cover the `VecDeque`'s valid range +trait RingSlices: Sized { + fn slice(self, from: usize, to: usize) -> Self; + fn split_at(self, i: usize) -> (Self, Self); + + fn ring_slices(buf: Self, head: usize, tail: usize) -> (Self, Self) { + let contiguous = tail <= head; + if contiguous { + let (empty, buf) = buf.split_at(0); + (buf.slice(tail, head), empty) + } else { + let (mid, right) = buf.split_at(tail); + let (left, _) = mid.split_at(head); + (right, left) + } + } +} + +impl<'a, T> RingSlices for &'a [T] { + fn slice(self, from: usize, to: usize) -> Self { + &self[from..to] + } + fn split_at(self, i: usize) -> (Self, Self) { + (*self).split_at(i) + } +} + +impl<'a, T> RingSlices for &'a mut [T] { + fn slice(self, from: usize, to: usize) -> Self { + &mut self[from..to] + } + fn split_at(self, i: usize) -> (Self, Self) { + (*self).split_at_mut(i) + } +} + +/// Calculate the number of elements left to be read in the buffer +#[inline] +fn count(tail: usize, head: usize, size: usize) -> usize { + // size is always a power of 2 + (head.wrapping_sub(tail)) & (size - 1) +} + +/// An iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`iter`] method on [`VecDeque`]. See its +/// documentation for more. +/// +/// [`iter`]: struct.VecDeque.html#method.iter +/// [`VecDeque`]: struct.VecDeque.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Iter<'a, T: 'a> { + ring: &'a [T], + tail: usize, + head: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Iter") + .field(&self.ring) + .field(&self.tail) + .field(&self.head) + .finish() + } +} + +// FIXME(#26925) Remove in favor of `#[derive(Clone)]` +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Clone for Iter<'a, T> { + fn clone(&self) -> Iter<'a, T> { + Iter { + ring: self.ring, + tail: self.tail, + head: self.head, + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for Iter<'a, T> { + type Item = &'a T; + + #[inline] + fn next(&mut self) -> Option<&'a T> { + if self.tail == self.head { + return None; + } + let tail = self.tail; + self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); + unsafe { Some(self.ring.get_unchecked(tail)) } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = count(self.tail, self.head, self.ring.len()); + (len, Some(len)) + } + + fn fold(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = front.iter().fold(accum, &mut f); + back.iter().fold(accum, &mut f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for Iter<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a T> { + if self.tail == self.head { + return None; + } + self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); + unsafe { Some(self.ring.get_unchecked(self.head)) } + } + + fn rfold(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = back.iter().rfold(accum, &mut f); + front.iter().rfold(accum, &mut f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for Iter<'a, T> { + fn is_empty(&self) -> bool { + self.head == self.tail + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for Iter<'a, T> {} + + +/// A mutable iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`iter_mut`] method on [`VecDeque`]. See its +/// documentation for more. +/// +/// [`iter_mut`]: struct.VecDeque.html#method.iter_mut +/// [`VecDeque`]: struct.VecDeque.html +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IterMut<'a, T: 'a> { + ring: &'a mut [T], + tail: usize, + head: usize, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("IterMut") + .field(&self.ring) + .field(&self.tail) + .field(&self.head) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> Iterator for IterMut<'a, T> { + type Item = &'a mut T; + + #[inline] + fn next(&mut self) -> Option<&'a mut T> { + if self.tail == self.head { + return None; + } + let tail = self.tail; + self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); + + unsafe { + let elem = self.ring.get_unchecked_mut(tail); + Some(&mut *(elem as *mut _)) + } + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = count(self.tail, self.head, self.ring.len()); + (len, Some(len)) + } + + fn fold(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = front.iter_mut().fold(accum, &mut f); + back.iter_mut().fold(accum, &mut f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { + #[inline] + fn next_back(&mut self) -> Option<&'a mut T> { + if self.tail == self.head { + return None; + } + self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); + + unsafe { + let elem = self.ring.get_unchecked_mut(self.head); + Some(&mut *(elem as *mut _)) + } + } + + fn rfold(self, mut accum: Acc, mut f: F) -> Acc + where F: FnMut(Acc, Self::Item) -> Acc + { + let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); + accum = back.iter_mut().rfold(accum, &mut f); + front.iter_mut().rfold(accum, &mut f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> ExactSizeIterator for IterMut<'a, T> { + fn is_empty(&self) -> bool { + self.head == self.tail + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T> FusedIterator for IterMut<'a, T> {} + +/// An owning iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`into_iter`] method on [`VecDeque`][`VecDeque`] +/// (provided by the `IntoIterator` trait). See its documentation for more. +/// +/// [`into_iter`]: struct.VecDeque.html#method.into_iter +/// [`VecDeque`]: struct.VecDeque.html +#[derive(Clone)] +#[stable(feature = "rust1", since = "1.0.0")] +pub struct IntoIter { + inner: VecDeque, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl fmt::Debug for IntoIter { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("IntoIter") + .field(&self.inner) + .finish() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Iterator for IntoIter { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.inner.pop_front() + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + let len = self.inner.len(); + (len, Some(len)) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl DoubleEndedIterator for IntoIter { + #[inline] + fn next_back(&mut self) -> Option { + self.inner.pop_back() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl ExactSizeIterator for IntoIter { + fn is_empty(&self) -> bool { + self.inner.is_empty() + } +} + +#[stable(feature = "fused", since = "1.26.0")] +impl FusedIterator for IntoIter {} + +/// A draining iterator over the elements of a `VecDeque`. +/// +/// This `struct` is created by the [`drain`] method on [`VecDeque`]. See its +/// documentation for more. +/// +/// [`drain`]: struct.VecDeque.html#method.drain +/// [`VecDeque`]: struct.VecDeque.html +#[stable(feature = "drain", since = "1.6.0")] +pub struct Drain<'a, T: 'a> { + after_tail: usize, + after_head: usize, + iter: Iter<'a, T>, + deque: NonNull>, +} + +#[stable(feature = "collection_debug", since = "1.17.0")] +impl<'a, T: 'a + fmt::Debug> fmt::Debug for Drain<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_tuple("Drain") + .field(&self.after_tail) + .field(&self.after_head) + .field(&self.iter) + .finish() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {} +#[stable(feature = "drain", since = "1.6.0")] +unsafe impl<'a, T: Send> Send for Drain<'a, T> {} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> Drop for Drain<'a, T> { + fn drop(&mut self) { + self.for_each(drop); + + let source_deque = unsafe { self.deque.as_mut() }; + + // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head + // + // T t h H + // [. . . o o x x o o . . .] + // + let orig_tail = source_deque.tail; + let drain_tail = source_deque.head; + let drain_head = self.after_tail; + let orig_head = self.after_head; + + let tail_len = count(orig_tail, drain_tail, source_deque.cap()); + let head_len = count(drain_head, orig_head, source_deque.cap()); + + // Restore the original head value + source_deque.head = orig_head; + + match (tail_len, head_len) { + (0, 0) => { + source_deque.head = 0; + source_deque.tail = 0; + } + (0, _) => { + source_deque.tail = drain_head; + } + (_, 0) => { + source_deque.head = drain_tail; + } + _ => unsafe { + if tail_len <= head_len { + source_deque.tail = source_deque.wrap_sub(drain_head, tail_len); + source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len); + } else { + source_deque.head = source_deque.wrap_add(drain_tail, head_len); + source_deque.wrap_copy(drain_tail, drain_head, head_len); + } + }, + } + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> Iterator for Drain<'a, T> { + type Item = T; + + #[inline] + fn next(&mut self) -> Option { + self.iter.next().map(|elt| unsafe { ptr::read(elt) }) + } + + #[inline] + fn size_hint(&self) -> (usize, Option) { + self.iter.size_hint() + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { + #[inline] + fn next_back(&mut self) -> Option { + self.iter.next_back().map(|elt| unsafe { ptr::read(elt) }) + } +} + +#[stable(feature = "drain", since = "1.6.0")] +impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> {} + +#[stable(feature = "fused", since = "1.26.0")] +impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for VecDeque { + fn eq(&self, other: &VecDeque) -> bool { + if self.len() != other.len() { + return false; + } + let (sa, sb) = self.as_slices(); + let (oa, ob) = other.as_slices(); + if sa.len() == oa.len() { + sa == oa && sb == ob + } else if sa.len() < oa.len() { + // Always divisible in three sections, for example: + // self: [a b c|d e f] + // other: [0 1 2 3|4 5] + // front = 3, mid = 1, + // [a b c] == [0 1 2] && [d] == [3] && [e f] == [4 5] + let front = sa.len(); + let mid = oa.len() - front; + + let (oa_front, oa_mid) = oa.split_at(front); + let (sb_mid, sb_back) = sb.split_at(mid); + debug_assert_eq!(sa.len(), oa_front.len()); + debug_assert_eq!(sb_mid.len(), oa_mid.len()); + debug_assert_eq!(sb_back.len(), ob.len()); + sa == oa_front && sb_mid == oa_mid && sb_back == ob + } else { + let front = oa.len(); + let mid = sa.len() - front; + + let (sa_front, sa_mid) = sa.split_at(front); + let (ob_mid, ob_back) = ob.split_at(mid); + debug_assert_eq!(sa_front.len(), oa.len()); + debug_assert_eq!(sa_mid.len(), ob_mid.len()); + debug_assert_eq!(sb.len(), ob_back.len()); + sa_front == oa && sa_mid == ob_mid && sb == ob_back + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for VecDeque {} + +macro_rules! __impl_slice_eq1 { + ($Lhs: ty, $Rhs: ty) => { + __impl_slice_eq1! { $Lhs, $Rhs, Sized } + }; + ($Lhs: ty, $Rhs: ty, $Bound: ident) => { + #[stable(feature = "vec-deque-partial-eq-slice", since = "1.17.0")] + impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq { + fn eq(&self, other: &$Rhs) -> bool { + if self.len() != other.len() { + return false; + } + let (sa, sb) = self.as_slices(); + let (oa, ob) = other[..].split_at(sa.len()); + sa == oa && sb == ob + } + } + } +} + +__impl_slice_eq1! { VecDeque, Vec } +__impl_slice_eq1! { VecDeque, &'b [B] } +__impl_slice_eq1! { VecDeque, &'b mut [B] } + +macro_rules! array_impls { + ($($N: expr)+) => { + $( + __impl_slice_eq1! { VecDeque, [B; $N] } + __impl_slice_eq1! { VecDeque, &'b [B; $N] } + __impl_slice_eq1! { VecDeque, &'b mut [B; $N] } + )+ + } +} + +array_impls! { + 0 1 2 3 4 5 6 7 8 9 + 10 11 12 13 14 15 16 17 18 19 + 20 21 22 23 24 25 26 27 28 29 + 30 31 32 +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for VecDeque { + fn partial_cmp(&self, other: &VecDeque) -> Option { + self.iter().partial_cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for VecDeque { + #[inline] + fn cmp(&self, other: &VecDeque) -> Ordering { + self.iter().cmp(other.iter()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for VecDeque { + fn hash(&self, state: &mut H) { + self.len().hash(state); + let (a, b) = self.as_slices(); + Hash::hash_slice(a, state); + Hash::hash_slice(b, state); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Index for VecDeque { + type Output = A; + + #[inline] + fn index(&self, index: usize) -> &A { + self.get(index).expect("Out of bounds access") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IndexMut for VecDeque { + #[inline] + fn index_mut(&mut self, index: usize) -> &mut A { + self.get_mut(index).expect("Out of bounds access") + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl FromIterator for VecDeque { + fn from_iter>(iter: T) -> VecDeque { + let iterator = iter.into_iter(); + let (lower, _) = iterator.size_hint(); + let mut deq = VecDeque::with_capacity(lower); + deq.extend(iterator); + deq + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl IntoIterator for VecDeque { + type Item = T; + type IntoIter = IntoIter; + + /// Consumes the `VecDeque` into a front-to-back iterator yielding elements by + /// value. + fn into_iter(self) -> IntoIter { + IntoIter { inner: self } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a VecDeque { + type Item = &'a T; + type IntoIter = Iter<'a, T>; + + fn into_iter(self) -> Iter<'a, T> { + self.iter() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl<'a, T> IntoIterator for &'a mut VecDeque { + type Item = &'a mut T; + type IntoIter = IterMut<'a, T>; + + fn into_iter(self) -> IterMut<'a, T> { + self.iter_mut() + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Extend for VecDeque { + fn extend>(&mut self, iter: T) { + for elt in iter { + self.push_back(elt); + } + } +} + +#[stable(feature = "extend_ref", since = "1.2.0")] +impl<'a, T: 'a + Copy> Extend<&'a T> for VecDeque { + fn extend>(&mut self, iter: I) { + self.extend(iter.into_iter().cloned()); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for VecDeque { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_list().entries(self).finish() + } +} + +#[stable(feature = "vecdeque_vec_conversions", since = "1.10.0")] +impl From> for VecDeque { + fn from(mut other: Vec) -> Self { + unsafe { + let other_buf = other.as_mut_ptr(); + let mut buf = RawVec::from_raw_parts(other_buf, other.capacity()); + let len = other.len(); + mem::forget(other); + + // We need to extend the buf if it's not a power of two, too small + // or doesn't have at least one free space + if !buf.cap().is_power_of_two() || (buf.cap() < (MINIMUM_CAPACITY + 1)) || + (buf.cap() == len) { + let cap = cmp::max(buf.cap() + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); + buf.reserve_exact(len, cap - len); + } + + VecDeque { + tail: 0, + head: len, + buf, + } + } + } +} + +#[stable(feature = "vecdeque_vec_conversions", since = "1.10.0")] +impl From> for Vec { + fn from(other: VecDeque) -> Self { + unsafe { + let buf = other.buf.ptr(); + let len = other.len(); + let tail = other.tail; + let head = other.head; + let cap = other.cap(); + + // Need to move the ring to the front of the buffer, as vec will expect this. + if other.is_contiguous() { + ptr::copy(buf.offset(tail as isize), buf, len); + } else { + if (tail - head) >= cmp::min(cap - tail, head) { + // There is enough free space in the centre for the shortest block so we can + // do this in at most three copy moves. + if (cap - tail) > head { + // right hand block is the long one; move that enough for the left + ptr::copy(buf.offset(tail as isize), + buf.offset((tail - head) as isize), + cap - tail); + // copy left in the end + ptr::copy(buf, buf.offset((cap - head) as isize), head); + // shift the new thing to the start + ptr::copy(buf.offset((tail - head) as isize), buf, len); + } else { + // left hand block is the long one, we can do it in two! + ptr::copy(buf, buf.offset((cap - tail) as isize), head); + ptr::copy(buf.offset(tail as isize), buf, cap - tail); + } + } else { + // Need to use N swaps to move the ring + // We can use the space at the end of the ring as a temp store + + let mut left_edge: usize = 0; + let mut right_edge: usize = tail; + + // The general problem looks like this + // GHIJKLM...ABCDEF - before any swaps + // ABCDEFM...GHIJKL - after 1 pass of swaps + // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store + // - then restart the algorithm with a new (smaller) store + // Sometimes the temp store is reached when the right edge is at the end + // of the buffer - this means we've hit the right order with fewer swaps! + // E.g + // EF..ABCD + // ABCDEF.. - after four only swaps we've finished + + while left_edge < len && right_edge != cap { + let mut right_offset = 0; + for i in left_edge..right_edge { + right_offset = (i - left_edge) % (cap - right_edge); + let src: isize = (right_edge + right_offset) as isize; + ptr::swap(buf.offset(i as isize), buf.offset(src)); + } + let n_ops = right_edge - left_edge; + left_edge += n_ops; + right_edge += right_offset + 1; + + } + } + + } + let out = Vec::from_raw_parts(buf, len, cap); + mem::forget(other); + out + } + } +} + +#[cfg(test)] +mod tests { + use test; + + use super::VecDeque; + + #[bench] + fn bench_push_back_100(b: &mut test::Bencher) { + let mut deq = VecDeque::with_capacity(101); + b.iter(|| { + for i in 0..100 { + deq.push_back(i); + } + deq.head = 0; + deq.tail = 0; + }) + } + + #[bench] + fn bench_push_front_100(b: &mut test::Bencher) { + let mut deq = VecDeque::with_capacity(101); + b.iter(|| { + for i in 0..100 { + deq.push_front(i); + } + deq.head = 0; + deq.tail = 0; + }) + } + + #[bench] + fn bench_pop_back_100(b: &mut test::Bencher) { + let mut deq = VecDeque::::with_capacity(101); + + b.iter(|| { + deq.head = 100; + deq.tail = 0; + while !deq.is_empty() { + test::black_box(deq.pop_back()); + } + }) + } + + #[bench] + fn bench_pop_front_100(b: &mut test::Bencher) { + let mut deq = VecDeque::::with_capacity(101); + + b.iter(|| { + deq.head = 100; + deq.tail = 0; + while !deq.is_empty() { + test::black_box(deq.pop_front()); + } + }) + } + + #[test] + fn test_swap_front_back_remove() { + fn test(back: bool) { + // This test checks that every single combination of tail position and length is tested. + // Capacity 15 should be large enough to cover every case. + let mut tester = VecDeque::with_capacity(15); + let usable_cap = tester.capacity(); + let final_len = usable_cap / 2; + + for len in 0..final_len { + let expected: VecDeque<_> = if back { + (0..len).collect() + } else { + (0..len).rev().collect() + }; + for tail_pos in 0..usable_cap { + tester.tail = tail_pos; + tester.head = tail_pos; + if back { + for i in 0..len * 2 { + tester.push_front(i); + } + for i in 0..len { + assert_eq!(tester.swap_remove_back(i), Some(len * 2 - 1 - i)); + } + } else { + for i in 0..len * 2 { + tester.push_back(i); + } + for i in 0..len { + let idx = tester.len() - 1 - i; + assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); + } + } + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } + test(true); + test(false); + } + + #[test] + fn test_insert() { + // This test checks that every single combination of tail position, length, and + // insertion position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + + // len is the length *after* insertion + for len in 1..cap { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..cap { + for to_insert in 0..len { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + if i != to_insert { + tester.push_back(i); + } + } + tester.insert(to_insert, to_insert); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } + } + + #[test] + fn test_remove() { + // This test checks that every single combination of tail position, length, and + // removal position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + // len is the length *after* removal + for len in 0..cap - 1 { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..cap { + for to_remove in 0..len + 1 { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + if i == to_remove { + tester.push_back(1234); + } + tester.push_back(i); + } + if to_remove == len { + tester.push_back(1234); + } + tester.remove(to_remove); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } + } + + #[test] + fn test_drain() { + let mut tester: VecDeque = VecDeque::with_capacity(7); + + let cap = tester.capacity(); + for len in 0..cap + 1 { + for tail in 0..cap + 1 { + for drain_start in 0..len + 1 { + for drain_end in drain_start..len + 1 { + tester.tail = tail; + tester.head = tail; + for i in 0..len { + tester.push_back(i); + } + + // Check that we drain the correct values + let drained: VecDeque<_> = tester.drain(drain_start..drain_end).collect(); + let drained_expected: VecDeque<_> = (drain_start..drain_end).collect(); + assert_eq!(drained, drained_expected); + + // We shouldn't have changed the capacity or made the + // head or tail out of bounds + assert_eq!(tester.capacity(), cap); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + + // We should see the correct values in the VecDeque + let expected: VecDeque<_> = (0..drain_start) + .chain(drain_end..len) + .collect(); + assert_eq!(expected, tester); + } + } + } + } + } + + #[test] + fn test_shrink_to_fit() { + // This test checks that every single combination of head and tail position, + // is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + tester.reserve(63); + let max_cap = tester.capacity(); + + for len in 0..cap + 1 { + // 0, 1, 2, .., len - 1 + let expected = (0..).take(len).collect::>(); + for tail_pos in 0..max_cap + 1 { + tester.tail = tail_pos; + tester.head = tail_pos; + tester.reserve(63); + for i in 0..len { + tester.push_back(i); + } + tester.shrink_to_fit(); + assert!(tester.capacity() <= cap); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert_eq!(tester, expected); + } + } + } + + #[test] + fn test_split_off() { + // This test checks that every single combination of tail position, length, and + // split position is tested. Capacity 15 should be large enough to cover every case. + + let mut tester = VecDeque::with_capacity(15); + // can't guarantee we got 15, so have to get what we got. + // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else + // this test isn't covering what it wants to + let cap = tester.capacity(); + + // len is the length *before* splitting + for len in 0..cap { + // index to split at + for at in 0..len + 1 { + // 0, 1, 2, .., at - 1 (may be empty) + let expected_self = (0..).take(at).collect::>(); + // at, at + 1, .., len - 1 (may be empty) + let expected_other = (at..).take(len - at).collect::>(); + + for tail_pos in 0..cap { + tester.tail = tail_pos; + tester.head = tail_pos; + for i in 0..len { + tester.push_back(i); + } + let result = tester.split_off(at); + assert!(tester.tail < tester.cap()); + assert!(tester.head < tester.cap()); + assert!(result.tail < result.cap()); + assert!(result.head < result.cap()); + assert_eq!(tester, expected_self); + assert_eq!(result, expected_other); + } + } + } + } + + #[test] + fn test_from_vec() { + use vec::Vec; + for cap in 0..35 { + for len in 0..cap + 1 { + let mut vec = Vec::with_capacity(cap); + vec.extend(0..len); + + let vd = VecDeque::from(vec.clone()); + assert!(vd.cap().is_power_of_two()); + assert_eq!(vd.len(), vec.len()); + assert!(vd.into_iter().eq(vec)); + } + } + } + + #[test] + fn test_vec_from_vecdeque() { + use vec::Vec; + + fn create_vec_and_test_convert(cap: usize, offset: usize, len: usize) { + let mut vd = VecDeque::with_capacity(cap); + for _ in 0..offset { + vd.push_back(0); + vd.pop_front(); + } + vd.extend(0..len); + + let vec: Vec<_> = Vec::from(vd.clone()); + assert_eq!(vec.len(), vd.len()); + assert!(vec.into_iter().eq(vd)); + } + + for cap_pwr in 0..7 { + // Make capacity as a (2^x)-1, so that the ring size is 2^x + let cap = (2i32.pow(cap_pwr) - 1) as usize; + + // In these cases there is enough free space to solve it with copies + for len in 0..((cap + 1) / 2) { + // Test contiguous cases + for offset in 0..(cap - len) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at end of buffer is bigger than block at start + for offset in (cap - len)..(cap - (len / 2)) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at start of buffer is bigger than block at end + for offset in (cap - (len / 2))..cap { + create_vec_and_test_convert(cap, offset, len) + } + } + + // Now there's not (necessarily) space to straighten the ring with simple copies, + // the ring will use swapping when: + // (cap + 1 - offset) > (cap + 1 - len) && (len - (cap + 1 - offset)) > (cap + 1 - len)) + // right block size > free space && left block size > free space + for len in ((cap + 1) / 2)..cap { + // Test contiguous cases + for offset in 0..(cap - len) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at end of buffer is bigger than block at start + for offset in (cap - len)..(cap - (len / 2)) { + create_vec_and_test_convert(cap, offset, len) + } + + // Test cases where block at start of buffer is bigger than block at end + for offset in (cap - (len / 2))..cap { + create_vec_and_test_convert(cap, offset, len) + } + } + } + } + +} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 585e34f5851..e8be9ecfa36 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -162,59 +162,24 @@ mod boxed { } #[cfg(test)] mod boxed_test; +pub mod collections; #[cfg(target_has_atomic = "ptr")] pub mod arc; pub mod rc; pub mod raw_vec; -// collections modules -pub mod binary_heap; -mod btree; pub mod borrow; pub mod fmt; -pub mod linked_list; pub mod slice; pub mod str; pub mod string; pub mod vec; -pub mod vec_deque; - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod btree_map { - //! A map based on a B-Tree. - #[stable(feature = "rust1", since = "1.0.0")] - pub use btree::map::*; -} - -#[stable(feature = "rust1", since = "1.0.0")] -pub mod btree_set { - //! A set based on a B-Tree. - #[stable(feature = "rust1", since = "1.0.0")] - pub use btree::set::*; -} #[cfg(not(test))] mod std { pub use core::ops; // RangeFull } -/// An intermediate trait for specialization of `Extend`. -#[doc(hidden)] -trait SpecExtend { - /// Extends `self` with the contents of the given iterator. - fn spec_extend(&mut self, iter: I); -} - -#[doc(no_inline)] -pub use binary_heap::BinaryHeap; -#[doc(no_inline)] -pub use btree_map::BTreeMap; -#[doc(no_inline)] -pub use btree_set::BTreeSet; -#[doc(no_inline)] -pub use linked_list::LinkedList; -#[doc(no_inline)] -pub use vec_deque::VecDeque; #[doc(no_inline)] pub use string::String; #[doc(no_inline)] diff --git a/src/liballoc/linked_list.rs b/src/liballoc/linked_list.rs deleted file mode 100644 index 9844de9a57d..00000000000 --- a/src/liballoc/linked_list.rs +++ /dev/null @@ -1,1486 +0,0 @@ -// Copyright 2012-2015 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A doubly-linked list with owned nodes. -//! -//! The `LinkedList` allows pushing and popping elements at either end -//! in constant time. -//! -//! Almost always it is better to use `Vec` or [`VecDeque`] instead of -//! [`LinkedList`]. In general, array-based containers are faster, -//! more memory efficient and make better use of CPU cache. -//! -//! [`LinkedList`]: ../linked_list/struct.LinkedList.html -//! [`VecDeque`]: ../vec_deque/struct.VecDeque.html - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::cmp::Ordering; -use core::fmt; -use core::hash::{Hasher, Hash}; -use core::iter::{FromIterator, FusedIterator}; -use core::marker::PhantomData; -use core::mem; -use core::ptr::NonNull; - -use boxed::Box; -use super::SpecExtend; - -/// A doubly-linked list with owned nodes. -/// -/// The `LinkedList` allows pushing and popping elements at either end -/// in constant time. -/// -/// Almost always it is better to use `Vec` or `VecDeque` instead of -/// `LinkedList`. In general, array-based containers are faster, -/// more memory efficient and make better use of CPU cache. -#[stable(feature = "rust1", since = "1.0.0")] -pub struct LinkedList { - head: Option>>, - tail: Option>>, - len: usize, - marker: PhantomData>>, -} - -struct Node { - next: Option>>, - prev: Option>>, - element: T, -} - -/// An iterator over the elements of a `LinkedList`. -/// -/// This `struct` is created by the [`iter`] method on [`LinkedList`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.LinkedList.html#method.iter -/// [`LinkedList`]: struct.LinkedList.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - head: Option>>, - tail: Option>>, - len: usize, - marker: PhantomData<&'a Node>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Iter") - .field(&self.len) - .finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Iter<'a, T> { - fn clone(&self) -> Self { - Iter { ..*self } - } -} - -/// A mutable iterator over the elements of a `LinkedList`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`LinkedList`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: struct.LinkedList.html#method.iter_mut -/// [`LinkedList`]: struct.LinkedList.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, T: 'a> { - list: &'a mut LinkedList, - head: Option>>, - tail: Option>>, - len: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("IterMut") - .field(&self.list) - .field(&self.len) - .finish() - } -} - -/// An owning iterator over the elements of a `LinkedList`. -/// -/// This `struct` is created by the [`into_iter`] method on [`LinkedList`][`LinkedList`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.LinkedList.html#method.into_iter -/// [`LinkedList`]: struct.LinkedList.html -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - list: LinkedList, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("IntoIter") - .field(&self.list) - .finish() - } -} - -impl Node { - fn new(element: T) -> Self { - Node { - next: None, - prev: None, - element, - } - } - - fn into_element(self: Box) -> T { - self.element - } -} - -// private methods -impl LinkedList { - /// Adds the given node to the front of the list. - #[inline] - fn push_front_node(&mut self, mut node: Box>) { - unsafe { - node.next = self.head; - node.prev = None; - let node = Some(Box::into_raw_non_null(node)); - - match self.head { - None => self.tail = node, - Some(mut head) => head.as_mut().prev = node, - } - - self.head = node; - self.len += 1; - } - } - - /// Removes and returns the node at the front of the list. - #[inline] - fn pop_front_node(&mut self) -> Option>> { - self.head.map(|node| unsafe { - let node = Box::from_raw(node.as_ptr()); - self.head = node.next; - - match self.head { - None => self.tail = None, - Some(mut head) => head.as_mut().prev = None, - } - - self.len -= 1; - node - }) - } - - /// Adds the given node to the back of the list. - #[inline] - fn push_back_node(&mut self, mut node: Box>) { - unsafe { - node.next = None; - node.prev = self.tail; - let node = Some(Box::into_raw_non_null(node)); - - match self.tail { - None => self.head = node, - Some(mut tail) => tail.as_mut().next = node, - } - - self.tail = node; - self.len += 1; - } - } - - /// Removes and returns the node at the back of the list. - #[inline] - fn pop_back_node(&mut self) -> Option>> { - self.tail.map(|node| unsafe { - let node = Box::from_raw(node.as_ptr()); - self.tail = node.prev; - - match self.tail { - None => self.head = None, - Some(mut tail) => tail.as_mut().next = None, - } - - self.len -= 1; - node - }) - } - - /// Unlinks the specified node from the current list. - /// - /// Warning: this will not check that the provided node belongs to the current list. - #[inline] - unsafe fn unlink_node(&mut self, mut node: NonNull>) { - let node = node.as_mut(); - - match node.prev { - Some(mut prev) => prev.as_mut().next = node.next.clone(), - // this node is the head node - None => self.head = node.next.clone(), - }; - - match node.next { - Some(mut next) => next.as_mut().prev = node.prev.clone(), - // this node is the tail node - None => self.tail = node.prev.clone(), - }; - - self.len -= 1; - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for LinkedList { - /// Creates an empty `LinkedList`. - #[inline] - fn default() -> Self { - Self::new() - } -} - -impl LinkedList { - /// Creates an empty `LinkedList`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let list: LinkedList = LinkedList::new(); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> Self { - LinkedList { - head: None, - tail: None, - len: 0, - marker: PhantomData, - } - } - - /// Moves all elements from `other` to the end of the list. - /// - /// This reuses all the nodes from `other` and moves them into `self`. After - /// this operation, `other` becomes empty. - /// - /// This operation should compute in O(1) time and O(1) memory. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut list1 = LinkedList::new(); - /// list1.push_back('a'); - /// - /// let mut list2 = LinkedList::new(); - /// list2.push_back('b'); - /// list2.push_back('c'); - /// - /// list1.append(&mut list2); - /// - /// let mut iter = list1.iter(); - /// assert_eq!(iter.next(), Some(&'a')); - /// assert_eq!(iter.next(), Some(&'b')); - /// assert_eq!(iter.next(), Some(&'c')); - /// assert!(iter.next().is_none()); - /// - /// assert!(list2.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn append(&mut self, other: &mut Self) { - match self.tail { - None => mem::swap(self, other), - Some(mut tail) => { - if let Some(mut other_head) = other.head.take() { - unsafe { - tail.as_mut().next = Some(other_head); - other_head.as_mut().prev = Some(tail); - } - - self.tail = other.tail.take(); - self.len += mem::replace(&mut other.len, 0); - } - } - } - } - - /// Provides a forward iterator. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList = LinkedList::new(); - /// - /// list.push_back(0); - /// list.push_back(1); - /// list.push_back(2); - /// - /// let mut iter = list.iter(); - /// assert_eq!(iter.next(), Some(&0)); - /// assert_eq!(iter.next(), Some(&1)); - /// assert_eq!(iter.next(), Some(&2)); - /// assert_eq!(iter.next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { - head: self.head, - tail: self.tail, - len: self.len, - marker: PhantomData, - } - } - - /// Provides a forward iterator with mutable references. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList = LinkedList::new(); - /// - /// list.push_back(0); - /// list.push_back(1); - /// list.push_back(2); - /// - /// for element in list.iter_mut() { - /// *element += 10; - /// } - /// - /// let mut iter = list.iter(); - /// assert_eq!(iter.next(), Some(&10)); - /// assert_eq!(iter.next(), Some(&11)); - /// assert_eq!(iter.next(), Some(&12)); - /// assert_eq!(iter.next(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut { - IterMut { - head: self.head, - tail: self.tail, - len: self.len, - list: self, - } - } - - /// Returns `true` if the `LinkedList` is empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// assert!(dl.is_empty()); - /// - /// dl.push_front("foo"); - /// assert!(!dl.is_empty()); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.head.is_none() - } - - /// Returns the length of the `LinkedList`. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// - /// dl.push_front(2); - /// assert_eq!(dl.len(), 1); - /// - /// dl.push_front(1); - /// assert_eq!(dl.len(), 2); - /// - /// dl.push_back(3); - /// assert_eq!(dl.len(), 3); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - self.len - } - - /// Removes all elements from the `LinkedList`. - /// - /// This operation should compute in O(n) time. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// - /// dl.push_front(2); - /// dl.push_front(1); - /// assert_eq!(dl.len(), 2); - /// assert_eq!(dl.front(), Some(&1)); - /// - /// dl.clear(); - /// assert_eq!(dl.len(), 0); - /// assert_eq!(dl.front(), None); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn clear(&mut self) { - *self = Self::new(); - } - - /// Returns `true` if the `LinkedList` contains an element equal to the - /// given value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList = LinkedList::new(); - /// - /// list.push_back(0); - /// list.push_back(1); - /// list.push_back(2); - /// - /// assert_eq!(list.contains(&0), true); - /// assert_eq!(list.contains(&10), false); - /// ``` - #[stable(feature = "linked_list_contains", since = "1.12.0")] - pub fn contains(&self, x: &T) -> bool - where T: PartialEq - { - self.iter().any(|e| e == x) - } - - /// Provides a reference to the front element, or `None` if the list is - /// empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// assert_eq!(dl.front(), None); - /// - /// dl.push_front(1); - /// assert_eq!(dl.front(), Some(&1)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn front(&self) -> Option<&T> { - unsafe { - self.head.as_ref().map(|node| &node.as_ref().element) - } - } - - /// Provides a mutable reference to the front element, or `None` if the list - /// is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// assert_eq!(dl.front(), None); - /// - /// dl.push_front(1); - /// assert_eq!(dl.front(), Some(&1)); - /// - /// match dl.front_mut() { - /// None => {}, - /// Some(x) => *x = 5, - /// } - /// assert_eq!(dl.front(), Some(&5)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn front_mut(&mut self) -> Option<&mut T> { - unsafe { - self.head.as_mut().map(|node| &mut node.as_mut().element) - } - } - - /// Provides a reference to the back element, or `None` if the list is - /// empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// assert_eq!(dl.back(), None); - /// - /// dl.push_back(1); - /// assert_eq!(dl.back(), Some(&1)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn back(&self) -> Option<&T> { - unsafe { - self.tail.as_ref().map(|node| &node.as_ref().element) - } - } - - /// Provides a mutable reference to the back element, or `None` if the list - /// is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// assert_eq!(dl.back(), None); - /// - /// dl.push_back(1); - /// assert_eq!(dl.back(), Some(&1)); - /// - /// match dl.back_mut() { - /// None => {}, - /// Some(x) => *x = 5, - /// } - /// assert_eq!(dl.back(), Some(&5)); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn back_mut(&mut self) -> Option<&mut T> { - unsafe { - self.tail.as_mut().map(|node| &mut node.as_mut().element) - } - } - - /// Adds an element first in the list. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut dl = LinkedList::new(); - /// - /// dl.push_front(2); - /// assert_eq!(dl.front().unwrap(), &2); - /// - /// dl.push_front(1); - /// assert_eq!(dl.front().unwrap(), &1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push_front(&mut self, elt: T) { - self.push_front_node(box Node::new(elt)); - } - - /// Removes the first element and returns it, or `None` if the list is - /// empty. - /// - /// This operation should compute in O(1) time. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut d = LinkedList::new(); - /// assert_eq!(d.pop_front(), None); - /// - /// d.push_front(1); - /// d.push_front(3); - /// assert_eq!(d.pop_front(), Some(3)); - /// assert_eq!(d.pop_front(), Some(1)); - /// assert_eq!(d.pop_front(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop_front(&mut self) -> Option { - self.pop_front_node().map(Node::into_element) - } - - /// Appends an element to the back of a list - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut d = LinkedList::new(); - /// d.push_back(1); - /// d.push_back(3); - /// assert_eq!(3, *d.back().unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push_back(&mut self, elt: T) { - self.push_back_node(box Node::new(elt)); - } - - /// Removes the last element from a list and returns it, or `None` if - /// it is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut d = LinkedList::new(); - /// assert_eq!(d.pop_back(), None); - /// d.push_back(1); - /// d.push_back(3); - /// assert_eq!(d.pop_back(), Some(3)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop_back(&mut self) -> Option { - self.pop_back_node().map(Node::into_element) - } - - /// Splits the list into two at the given index. Returns everything after the given index, - /// including the index. - /// - /// This operation should compute in O(n) time. - /// - /// # Panics - /// - /// Panics if `at > len`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::LinkedList; - /// - /// let mut d = LinkedList::new(); - /// - /// d.push_front(1); - /// d.push_front(2); - /// d.push_front(3); - /// - /// let mut splitted = d.split_off(2); - /// - /// assert_eq!(splitted.pop_front(), Some(1)); - /// assert_eq!(splitted.pop_front(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn split_off(&mut self, at: usize) -> LinkedList { - let len = self.len(); - assert!(at <= len, "Cannot split off at a nonexistent index"); - if at == 0 { - return mem::replace(self, Self::new()); - } else if at == len { - return Self::new(); - } - - // Below, we iterate towards the `i-1`th node, either from the start or the end, - // depending on which would be faster. - let split_node = if at - 1 <= len - 1 - (at - 1) { - let mut iter = self.iter_mut(); - // instead of skipping using .skip() (which creates a new struct), - // we skip manually so we can access the head field without - // depending on implementation details of Skip - for _ in 0..at - 1 { - iter.next(); - } - iter.head - } else { - // better off starting from the end - let mut iter = self.iter_mut(); - for _ in 0..len - 1 - (at - 1) { - iter.next_back(); - } - iter.tail - }; - - // The split node is the new tail node of the first part and owns - // the head of the second part. - let second_part_head; - - unsafe { - second_part_head = split_node.unwrap().as_mut().next.take(); - if let Some(mut head) = second_part_head { - head.as_mut().prev = None; - } - } - - let second_part = LinkedList { - head: second_part_head, - tail: self.tail, - len: len - at, - marker: PhantomData, - }; - - // Fix the tail ptr of the first part - self.tail = split_node; - self.len = at; - - second_part - } - - /// Creates an iterator which uses a closure to determine if an element should be removed. - /// - /// If the closure returns true, then the element is removed and yielded. - /// If the closure returns false, the element will remain in the list and will not be yielded - /// by the iterator. - /// - /// Note that `drain_filter` lets you mutate every element in the filter closure, regardless of - /// whether you choose to keep or remove it. - /// - /// # Examples - /// - /// Splitting a list into evens and odds, reusing the original list: - /// - /// ``` - /// #![feature(drain_filter)] - /// use std::collections::LinkedList; - /// - /// let mut numbers: LinkedList = LinkedList::new(); - /// numbers.extend(&[1, 2, 3, 4, 5, 6, 8, 9, 11, 13, 14, 15]); - /// - /// let evens = numbers.drain_filter(|x| *x % 2 == 0).collect::>(); - /// let odds = numbers; - /// - /// assert_eq!(evens.into_iter().collect::>(), vec![2, 4, 6, 8, 14]); - /// assert_eq!(odds.into_iter().collect::>(), vec![1, 3, 5, 9, 11, 13, 15]); - /// ``` - #[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] - pub fn drain_filter(&mut self, filter: F) -> DrainFilter - where F: FnMut(&mut T) -> bool - { - // avoid borrow issues. - let it = self.head; - let old_len = self.len; - - DrainFilter { - list: self, - it: it, - pred: filter, - idx: 0, - old_len: old_len, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T> Drop for LinkedList { - fn drop(&mut self) { - while let Some(_) = self.pop_front_node() {} - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - if self.len == 0 { - None - } else { - self.head.map(|node| unsafe { - // Need an unbound lifetime to get 'a - let node = &*node.as_ptr(); - self.len -= 1; - self.head = node.next; - &node.element - }) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - (self.len, Some(self.len)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - if self.len == 0 { - None - } else { - self.tail.map(|node| unsafe { - // Need an unbound lifetime to get 'a - let node = &*node.as_ptr(); - self.len -= 1; - self.tail = node.prev; - &node.element - }) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for Iter<'a, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for Iter<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - #[inline] - fn next(&mut self) -> Option<&'a mut T> { - if self.len == 0 { - None - } else { - self.head.map(|node| unsafe { - // Need an unbound lifetime to get 'a - let node = &mut *node.as_ptr(); - self.len -= 1; - self.head = node.next; - &mut node.element - }) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - (self.len, Some(self.len)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut T> { - if self.len == 0 { - None - } else { - self.tail.map(|node| unsafe { - // Need an unbound lifetime to get 'a - let node = &mut *node.as_ptr(); - self.len -= 1; - self.tail = node.prev; - &mut node.element - }) - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for IterMut<'a, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for IterMut<'a, T> {} - -impl<'a, T> IterMut<'a, T> { - /// Inserts the given element just after the element most recently returned by `.next()`. - /// The inserted element does not appear in the iteration. - /// - /// # Examples - /// - /// ``` - /// #![feature(linked_list_extras)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList<_> = vec![1, 3, 4].into_iter().collect(); - /// - /// { - /// let mut it = list.iter_mut(); - /// assert_eq!(it.next().unwrap(), &1); - /// // insert `2` after `1` - /// it.insert_next(2); - /// } - /// { - /// let vec: Vec<_> = list.into_iter().collect(); - /// assert_eq!(vec, [1, 2, 3, 4]); - /// } - /// ``` - #[inline] - #[unstable(feature = "linked_list_extras", - reason = "this is probably better handled by a cursor type -- we'll see", - issue = "27794")] - pub fn insert_next(&mut self, element: T) { - match self.head { - None => self.list.push_back(element), - Some(mut head) => unsafe { - let mut prev = match head.as_ref().prev { - None => return self.list.push_front(element), - Some(prev) => prev, - }; - - let node = Some(Box::into_raw_non_null(box Node { - next: Some(head), - prev: Some(prev), - element, - })); - - prev.as_mut().next = node; - head.as_mut().prev = node; - - self.list.len += 1; - }, - } - } - - /// Provides a reference to the next element, without changing the iterator. - /// - /// # Examples - /// - /// ``` - /// #![feature(linked_list_extras)] - /// - /// use std::collections::LinkedList; - /// - /// let mut list: LinkedList<_> = vec![1, 2, 3].into_iter().collect(); - /// - /// let mut it = list.iter_mut(); - /// assert_eq!(it.next().unwrap(), &1); - /// assert_eq!(it.peek_next().unwrap(), &2); - /// // We just peeked at 2, so it was not consumed from the iterator. - /// assert_eq!(it.next().unwrap(), &2); - /// ``` - #[inline] - #[unstable(feature = "linked_list_extras", - reason = "this is probably better handled by a cursor type -- we'll see", - issue = "27794")] - pub fn peek_next(&mut self) -> Option<&mut T> { - if self.len == 0 { - None - } else { - unsafe { - self.head.as_mut().map(|node| &mut node.as_mut().element) - } - } - } -} - -/// An iterator produced by calling `drain_filter` on LinkedList. -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -pub struct DrainFilter<'a, T: 'a, F: 'a> - where F: FnMut(&mut T) -> bool, -{ - list: &'a mut LinkedList, - it: Option>>, - pred: F, - idx: usize, - old_len: usize, -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl<'a, T, F> Iterator for DrainFilter<'a, T, F> - where F: FnMut(&mut T) -> bool, -{ - type Item = T; - - fn next(&mut self) -> Option { - while let Some(mut node) = self.it { - unsafe { - self.it = node.as_ref().next; - self.idx += 1; - - if (self.pred)(&mut node.as_mut().element) { - self.list.unlink_node(node); - return Some(Box::from_raw(node.as_ptr()).element); - } - } - } - - None - } - - fn size_hint(&self) -> (usize, Option) { - (0, Some(self.old_len - self.idx)) - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl<'a, T, F> Drop for DrainFilter<'a, T, F> - where F: FnMut(&mut T) -> bool, -{ - fn drop(&mut self) { - self.for_each(drop); - } -} - -#[unstable(feature = "drain_filter", reason = "recently added", issue = "43244")] -impl<'a, T: 'a + fmt::Debug, F> fmt::Debug for DrainFilter<'a, T, F> - where F: FnMut(&mut T) -> bool -{ - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("DrainFilter") - .field(&self.list) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.list.pop_front() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - (self.list.len, Some(self.list.len)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.list.pop_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter {} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for LinkedList { - fn from_iter>(iter: I) -> Self { - let mut list = Self::new(); - list.extend(iter); - list - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for LinkedList { - type Item = T; - type IntoIter = IntoIter; - - /// Consumes the list into an iterator yielding elements by value. - #[inline] - fn into_iter(self) -> IntoIter { - IntoIter { list: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a LinkedList { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a mut LinkedList { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for LinkedList { - fn extend>(&mut self, iter: I) { - >::spec_extend(self, iter); - } -} - -impl SpecExtend for LinkedList { - default fn spec_extend(&mut self, iter: I) { - for elt in iter { - self.push_back(elt); - } - } -} - -impl SpecExtend> for LinkedList { - fn spec_extend(&mut self, ref mut other: LinkedList) { - self.append(other); - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Copy> Extend<&'a T> for LinkedList { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for LinkedList { - fn eq(&self, other: &Self) -> bool { - self.len() == other.len() && self.iter().eq(other) - } - - fn ne(&self, other: &Self) -> bool { - self.len() != other.len() || self.iter().ne(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for LinkedList {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for LinkedList { - fn partial_cmp(&self, other: &Self) -> Option { - self.iter().partial_cmp(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for LinkedList { - #[inline] - fn cmp(&self, other: &Self) -> Ordering { - self.iter().cmp(other) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for LinkedList { - fn clone(&self) -> Self { - self.iter().cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for LinkedList { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self).finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for LinkedList { - fn hash(&self, state: &mut H) { - self.len().hash(state); - for elt in self { - elt.hash(state); - } - } -} - -// Ensure that `LinkedList` and its read-only iterators are covariant in their type parameters. -#[allow(dead_code)] -fn assert_covariance() { - fn a<'a>(x: LinkedList<&'static str>) -> LinkedList<&'a str> { - x - } - fn b<'i, 'a>(x: Iter<'i, &'static str>) -> Iter<'i, &'a str> { - x - } - fn c<'a>(x: IntoIter<&'static str>) -> IntoIter<&'a str> { - x - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for LinkedList {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for LinkedList {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, T: Sync> Send for Iter<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, T: Sync> Sync for Iter<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, T: Send> Send for IterMut<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<'a, T: Sync> Sync for IterMut<'a, T> {} - -#[cfg(test)] -mod tests { - use std::thread; - use std::vec::Vec; - - use rand::{thread_rng, Rng}; - - use super::{LinkedList, Node}; - - #[cfg(test)] - fn list_from(v: &[T]) -> LinkedList { - v.iter().cloned().collect() - } - - pub fn check_links(list: &LinkedList) { - unsafe { - let mut len = 0; - let mut last_ptr: Option<&Node> = None; - let mut node_ptr: &Node; - match list.head { - None => { - // tail node should also be None. - assert!(list.tail.is_none()); - assert_eq!(0, list.len); - return; - } - Some(node) => node_ptr = &*node.as_ptr(), - } - loop { - match (last_ptr, node_ptr.prev) { - (None, None) => {} - (None, _) => panic!("prev link for head"), - (Some(p), Some(pptr)) => { - assert_eq!(p as *const Node, pptr.as_ptr() as *const Node); - } - _ => panic!("prev link is none, not good"), - } - match node_ptr.next { - Some(next) => { - last_ptr = Some(node_ptr); - node_ptr = &*next.as_ptr(); - len += 1; - } - None => { - len += 1; - break; - } - } - } - - // verify that the tail node points to the last node. - let tail = list.tail.as_ref().expect("some tail node").as_ref(); - assert_eq!(tail as *const Node, node_ptr as *const Node); - // check that len matches interior links. - assert_eq!(len, list.len); - } - } - - #[test] - fn test_append() { - // Empty to empty - { - let mut m = LinkedList::::new(); - let mut n = LinkedList::new(); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 0); - assert_eq!(n.len(), 0); - } - // Non-empty to empty - { - let mut m = LinkedList::new(); - let mut n = LinkedList::new(); - n.push_back(2); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 1); - assert_eq!(m.pop_back(), Some(2)); - assert_eq!(n.len(), 0); - check_links(&m); - } - // Empty to non-empty - { - let mut m = LinkedList::new(); - let mut n = LinkedList::new(); - m.push_back(2); - m.append(&mut n); - check_links(&m); - assert_eq!(m.len(), 1); - assert_eq!(m.pop_back(), Some(2)); - check_links(&m); - } - - // Non-empty to non-empty - let v = vec![1, 2, 3, 4, 5]; - let u = vec![9, 8, 1, 2, 3, 4, 5]; - let mut m = list_from(&v); - let mut n = list_from(&u); - m.append(&mut n); - check_links(&m); - let mut sum = v; - sum.extend_from_slice(&u); - assert_eq!(sum.len(), m.len()); - for elt in sum { - assert_eq!(m.pop_front(), Some(elt)) - } - assert_eq!(n.len(), 0); - // let's make sure it's working properly, since we - // did some direct changes to private members - n.push_back(3); - assert_eq!(n.len(), 1); - assert_eq!(n.pop_front(), Some(3)); - check_links(&n); - } - - #[test] - fn test_insert_prev() { - let mut m = list_from(&[0, 2, 4, 6, 8]); - let len = m.len(); - { - let mut it = m.iter_mut(); - it.insert_next(-2); - loop { - match it.next() { - None => break, - Some(elt) => { - it.insert_next(*elt + 1); - match it.peek_next() { - Some(x) => assert_eq!(*x, *elt + 2), - None => assert_eq!(8, *elt), - } - } - } - } - it.insert_next(0); - it.insert_next(1); - } - check_links(&m); - assert_eq!(m.len(), 3 + len * 2); - assert_eq!(m.into_iter().collect::>(), - [-2, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 1]); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn test_send() { - let n = list_from(&[1, 2, 3]); - thread::spawn(move || { - check_links(&n); - let a: &[_] = &[&1, &2, &3]; - assert_eq!(a, &*n.iter().collect::>()); - }) - .join() - .ok() - .unwrap(); - } - - #[test] - fn test_fuzz() { - for _ in 0..25 { - fuzz_test(3); - fuzz_test(16); - fuzz_test(189); - } - } - - #[test] - fn test_26021() { - // There was a bug in split_off that failed to null out the RHS's head's prev ptr. - // This caused the RHS's dtor to walk up into the LHS at drop and delete all of - // its nodes. - // - // https://github.com/rust-lang/rust/issues/26021 - let mut v1 = LinkedList::new(); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - let _ = v1.split_off(3); // Dropping this now should not cause laundry consumption - assert_eq!(v1.len(), 3); - - assert_eq!(v1.iter().len(), 3); - assert_eq!(v1.iter().collect::>().len(), 3); - } - - #[test] - fn test_split_off() { - let mut v1 = LinkedList::new(); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - v1.push_front(1); - - // test all splits - for ix in 0..1 + v1.len() { - let mut a = v1.clone(); - let b = a.split_off(ix); - check_links(&a); - check_links(&b); - a.extend(b); - assert_eq!(v1, a); - } - } - - #[cfg(test)] - fn fuzz_test(sz: i32) { - let mut m: LinkedList<_> = LinkedList::new(); - let mut v = vec![]; - for i in 0..sz { - check_links(&m); - let r: u8 = thread_rng().next_u32() as u8; - match r % 6 { - 0 => { - m.pop_back(); - v.pop(); - } - 1 => { - if !v.is_empty() { - m.pop_front(); - v.remove(0); - } - } - 2 | 4 => { - m.push_front(-i); - v.insert(0, -i); - } - 3 | 5 | _ => { - m.push_back(i); - v.push(i); - } - } - } - - check_links(&m); - - let mut i = 0; - for (a, &b) in m.into_iter().zip(&v) { - i += 1; - assert_eq!(a, b); - } - assert_eq!(i, v.len()); - } - - #[test] - fn drain_filter_test() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|v| *v < 4).collect::>(); - - check_links(&m); - - assert_eq!(deleted, &[1, 2, 3]); - assert_eq!(m.into_iter().collect::>(), &[4, 5, 6]); - } - - #[test] - fn drain_to_empty_test() { - let mut m: LinkedList = LinkedList::new(); - m.extend(&[1, 2, 3, 4, 5, 6]); - let deleted = m.drain_filter(|_| true).collect::>(); - - check_links(&m); - - assert_eq!(deleted, &[1, 2, 3, 4, 5, 6]); - assert_eq!(m.into_iter().collect::>(), &[]); - } -} diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index ec9c39c916c..bb99d0401d3 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -51,7 +51,6 @@ use boxed::Box; use slice::{SliceConcatExt, SliceIndex}; use string::String; use vec::Vec; -use vec_deque::VecDeque; #[stable(feature = "rust1", since = "1.0.0")] pub use core::str::{FromStr, Utf8Error}; diff --git a/src/liballoc/vec_deque.rs b/src/liballoc/vec_deque.rs deleted file mode 100644 index e917a65c9c5..00000000000 --- a/src/liballoc/vec_deque.rs +++ /dev/null @@ -1,2970 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -//! A double-ended queue implemented with a growable ring buffer. -//! -//! This queue has `O(1)` amortized inserts and removals from both ends of the -//! container. It also has `O(1)` indexing like a vector. The contained elements -//! are not required to be copyable, and the queue will be sendable if the -//! contained type is sendable. - -#![stable(feature = "rust1", since = "1.0.0")] - -use core::cmp::Ordering; -use core::fmt; -use core::iter::{repeat, FromIterator, FusedIterator}; -use core::mem; -use core::ops::Bound::{Excluded, Included, Unbounded}; -use core::ops::{Index, IndexMut, RangeBounds}; -use core::ptr; -use core::ptr::NonNull; -use core::slice; - -use core::hash::{Hash, Hasher}; -use core::cmp; - -use alloc::CollectionAllocErr; -use raw_vec::RawVec; -use vec::Vec; - -const INITIAL_CAPACITY: usize = 7; // 2^3 - 1 -const MINIMUM_CAPACITY: usize = 1; // 2 - 1 -#[cfg(target_pointer_width = "32")] -const MAXIMUM_ZST_CAPACITY: usize = 1 << (32 - 1); // Largest possible power of two -#[cfg(target_pointer_width = "64")] -const MAXIMUM_ZST_CAPACITY: usize = 1 << (64 - 1); // Largest possible power of two - -/// A double-ended queue implemented with a growable ring buffer. -/// -/// The "default" usage of this type as a queue is to use [`push_back`] to add to -/// the queue, and [`pop_front`] to remove from the queue. [`extend`] and [`append`] -/// push onto the back in this manner, and iterating over `VecDeque` goes front -/// to back. -/// -/// [`push_back`]: #method.push_back -/// [`pop_front`]: #method.pop_front -/// [`extend`]: #method.extend -/// [`append`]: #method.append -#[stable(feature = "rust1", since = "1.0.0")] -pub struct VecDeque { - // tail and head are pointers into the buffer. Tail always points - // to the first element that could be read, Head always points - // to where data should be written. - // If tail == head the buffer is empty. The length of the ringbuffer - // is defined as the distance between the two. - tail: usize, - head: usize, - buf: RawVec, -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for VecDeque { - fn clone(&self) -> VecDeque { - self.iter().cloned().collect() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T> Drop for VecDeque { - fn drop(&mut self) { - let (front, back) = self.as_mut_slices(); - unsafe { - // use drop for [T] - ptr::drop_in_place(front); - ptr::drop_in_place(back); - } - // RawVec handles deallocation - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for VecDeque { - /// Creates an empty `VecDeque`. - #[inline] - fn default() -> VecDeque { - VecDeque::new() - } -} - -impl VecDeque { - /// Marginally more convenient - #[inline] - fn ptr(&self) -> *mut T { - self.buf.ptr() - } - - /// Marginally more convenient - #[inline] - fn cap(&self) -> usize { - if mem::size_of::() == 0 { - // For zero sized types, we are always at maximum capacity - MAXIMUM_ZST_CAPACITY - } else { - self.buf.cap() - } - } - - /// Turn ptr into a slice - #[inline] - unsafe fn buffer_as_slice(&self) -> &[T] { - slice::from_raw_parts(self.ptr(), self.cap()) - } - - /// Turn ptr into a mut slice - #[inline] - unsafe fn buffer_as_mut_slice(&mut self) -> &mut [T] { - slice::from_raw_parts_mut(self.ptr(), self.cap()) - } - - /// Moves an element out of the buffer - #[inline] - unsafe fn buffer_read(&mut self, off: usize) -> T { - ptr::read(self.ptr().offset(off as isize)) - } - - /// Writes an element into the buffer, moving it. - #[inline] - unsafe fn buffer_write(&mut self, off: usize, value: T) { - ptr::write(self.ptr().offset(off as isize), value); - } - - /// Returns `true` if and only if the buffer is at full capacity. - #[inline] - fn is_full(&self) -> bool { - self.cap() - self.len() == 1 - } - - /// Returns the index in the underlying buffer for a given logical element - /// index. - #[inline] - fn wrap_index(&self, idx: usize) -> usize { - wrap_index(idx, self.cap()) - } - - /// Returns the index in the underlying buffer for a given logical element - /// index + addend. - #[inline] - fn wrap_add(&self, idx: usize, addend: usize) -> usize { - wrap_index(idx.wrapping_add(addend), self.cap()) - } - - /// Returns the index in the underlying buffer for a given logical element - /// index - subtrahend. - #[inline] - fn wrap_sub(&self, idx: usize, subtrahend: usize) -> usize { - wrap_index(idx.wrapping_sub(subtrahend), self.cap()) - } - - /// Copies a contiguous block of memory len long from src to dst - #[inline] - unsafe fn copy(&self, dst: usize, src: usize, len: usize) { - debug_assert!(dst + len <= self.cap(), - "cpy dst={} src={} len={} cap={}", - dst, - src, - len, - self.cap()); - debug_assert!(src + len <= self.cap(), - "cpy dst={} src={} len={} cap={}", - dst, - src, - len, - self.cap()); - ptr::copy(self.ptr().offset(src as isize), - self.ptr().offset(dst as isize), - len); - } - - /// Copies a contiguous block of memory len long from src to dst - #[inline] - unsafe fn copy_nonoverlapping(&self, dst: usize, src: usize, len: usize) { - debug_assert!(dst + len <= self.cap(), - "cno dst={} src={} len={} cap={}", - dst, - src, - len, - self.cap()); - debug_assert!(src + len <= self.cap(), - "cno dst={} src={} len={} cap={}", - dst, - src, - len, - self.cap()); - ptr::copy_nonoverlapping(self.ptr().offset(src as isize), - self.ptr().offset(dst as isize), - len); - } - - /// Copies a potentially wrapping block of memory len long from src to dest. - /// (abs(dst - src) + len) must be no larger than cap() (There must be at - /// most one continuous overlapping region between src and dest). - unsafe fn wrap_copy(&self, dst: usize, src: usize, len: usize) { - #[allow(dead_code)] - fn diff(a: usize, b: usize) -> usize { - if a <= b { b - a } else { a - b } - } - debug_assert!(cmp::min(diff(dst, src), self.cap() - diff(dst, src)) + len <= self.cap(), - "wrc dst={} src={} len={} cap={}", - dst, - src, - len, - self.cap()); - - if src == dst || len == 0 { - return; - } - - let dst_after_src = self.wrap_sub(dst, src) < len; - - let src_pre_wrap_len = self.cap() - src; - let dst_pre_wrap_len = self.cap() - dst; - let src_wraps = src_pre_wrap_len < len; - let dst_wraps = dst_pre_wrap_len < len; - - match (dst_after_src, src_wraps, dst_wraps) { - (_, false, false) => { - // src doesn't wrap, dst doesn't wrap - // - // S . . . - // 1 [_ _ A A B B C C _] - // 2 [_ _ A A A A B B _] - // D . . . - // - self.copy(dst, src, len); - } - (false, false, true) => { - // dst before src, src doesn't wrap, dst wraps - // - // S . . . - // 1 [A A B B _ _ _ C C] - // 2 [A A B B _ _ _ A A] - // 3 [B B B B _ _ _ A A] - // . . D . - // - self.copy(dst, src, dst_pre_wrap_len); - self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); - } - (true, false, true) => { - // src before dst, src doesn't wrap, dst wraps - // - // S . . . - // 1 [C C _ _ _ A A B B] - // 2 [B B _ _ _ A A B B] - // 3 [B B _ _ _ A A A A] - // . . D . - // - self.copy(0, src + dst_pre_wrap_len, len - dst_pre_wrap_len); - self.copy(dst, src, dst_pre_wrap_len); - } - (false, true, false) => { - // dst before src, src wraps, dst doesn't wrap - // - // . . S . - // 1 [C C _ _ _ A A B B] - // 2 [C C _ _ _ B B B B] - // 3 [C C _ _ _ B B C C] - // D . . . - // - self.copy(dst, src, src_pre_wrap_len); - self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); - } - (true, true, false) => { - // src before dst, src wraps, dst doesn't wrap - // - // . . S . - // 1 [A A B B _ _ _ C C] - // 2 [A A A A _ _ _ C C] - // 3 [C C A A _ _ _ C C] - // D . . . - // - self.copy(dst + src_pre_wrap_len, 0, len - src_pre_wrap_len); - self.copy(dst, src, src_pre_wrap_len); - } - (false, true, true) => { - // dst before src, src wraps, dst wraps - // - // . . . S . - // 1 [A B C D _ E F G H] - // 2 [A B C D _ E G H H] - // 3 [A B C D _ E G H A] - // 4 [B C C D _ E G H A] - // . . D . . - // - debug_assert!(dst_pre_wrap_len > src_pre_wrap_len); - let delta = dst_pre_wrap_len - src_pre_wrap_len; - self.copy(dst, src, src_pre_wrap_len); - self.copy(dst + src_pre_wrap_len, 0, delta); - self.copy(0, delta, len - dst_pre_wrap_len); - } - (true, true, true) => { - // src before dst, src wraps, dst wraps - // - // . . S . . - // 1 [A B C D _ E F G H] - // 2 [A A B D _ E F G H] - // 3 [H A B D _ E F G H] - // 4 [H A B D _ E F F G] - // . . . D . - // - debug_assert!(src_pre_wrap_len > dst_pre_wrap_len); - let delta = src_pre_wrap_len - dst_pre_wrap_len; - self.copy(delta, 0, len - src_pre_wrap_len); - self.copy(0, self.cap() - delta, delta); - self.copy(dst, src, dst_pre_wrap_len); - } - } - } - - /// Frobs the head and tail sections around to handle the fact that we - /// just reallocated. Unsafe because it trusts old_cap. - #[inline] - unsafe fn handle_cap_increase(&mut self, old_cap: usize) { - let new_cap = self.cap(); - - // Move the shortest contiguous section of the ring buffer - // T H - // [o o o o o o o . ] - // T H - // A [o o o o o o o . . . . . . . . . ] - // H T - // [o o . o o o o o ] - // T H - // B [. . . o o o o o o o . . . . . . ] - // H T - // [o o o o o . o o ] - // H T - // C [o o o o o . . . . . . . . . o o ] - - if self.tail <= self.head { - // A - // Nop - } else if self.head < old_cap - self.tail { - // B - self.copy_nonoverlapping(old_cap, 0, self.head); - self.head += old_cap; - debug_assert!(self.head > self.tail); - } else { - // C - let new_tail = new_cap - (old_cap - self.tail); - self.copy_nonoverlapping(new_tail, self.tail, old_cap - self.tail); - self.tail = new_tail; - debug_assert!(self.head < self.tail); - } - debug_assert!(self.head < self.cap()); - debug_assert!(self.tail < self.cap()); - debug_assert!(self.cap().count_ones() == 1); - } -} - -impl VecDeque { - /// Creates an empty `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let vector: VecDeque = VecDeque::new(); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new() -> VecDeque { - VecDeque::with_capacity(INITIAL_CAPACITY) - } - - /// Creates an empty `VecDeque` with space for at least `n` elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let vector: VecDeque = VecDeque::with_capacity(10); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn with_capacity(n: usize) -> VecDeque { - // +1 since the ringbuffer always leaves one space empty - let cap = cmp::max(n + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); - assert!(cap > n, "capacity overflow"); - - VecDeque { - tail: 0, - head: 0, - buf: RawVec::with_capacity(cap), - } - } - - /// Retrieves an element in the `VecDeque` by index. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(3); - /// buf.push_back(4); - /// buf.push_back(5); - /// assert_eq!(buf.get(1), Some(&4)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get(&self, index: usize) -> Option<&T> { - if index < self.len() { - let idx = self.wrap_add(self.tail, index); - unsafe { Some(&*self.ptr().offset(idx as isize)) } - } else { - None - } - } - - /// Retrieves an element in the `VecDeque` mutably by index. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(3); - /// buf.push_back(4); - /// buf.push_back(5); - /// if let Some(elem) = buf.get_mut(1) { - /// *elem = 7; - /// } - /// - /// assert_eq!(buf[1], 7); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn get_mut(&mut self, index: usize) -> Option<&mut T> { - if index < self.len() { - let idx = self.wrap_add(self.tail, index); - unsafe { Some(&mut *self.ptr().offset(idx as isize)) } - } else { - None - } - } - - /// Swaps elements at indices `i` and `j`. - /// - /// `i` and `j` may be equal. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Panics - /// - /// Panics if either index is out of bounds. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(3); - /// buf.push_back(4); - /// buf.push_back(5); - /// assert_eq!(buf, [3, 4, 5]); - /// buf.swap(0, 2); - /// assert_eq!(buf, [5, 4, 3]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn swap(&mut self, i: usize, j: usize) { - assert!(i < self.len()); - assert!(j < self.len()); - let ri = self.wrap_add(self.tail, i); - let rj = self.wrap_add(self.tail, j); - unsafe { - ptr::swap(self.ptr().offset(ri as isize), - self.ptr().offset(rj as isize)) - } - } - - /// Returns the number of elements the `VecDeque` can hold without - /// reallocating. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let buf: VecDeque = VecDeque::with_capacity(10); - /// assert!(buf.capacity() >= 10); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn capacity(&self) -> usize { - self.cap() - 1 - } - - /// Reserves the minimum capacity for exactly `additional` more elements to be inserted in the - /// given `VecDeque`. Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it requests. Therefore - /// capacity can not be relied upon to be precisely minimal. Prefer [`reserve`] if future - /// insertions are expected. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf: VecDeque = vec![1].into_iter().collect(); - /// buf.reserve_exact(10); - /// assert!(buf.capacity() >= 11); - /// ``` - /// - /// [`reserve`]: #method.reserve - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve_exact(&mut self, additional: usize) { - self.reserve(additional); - } - - /// Reserves capacity for at least `additional` more elements to be inserted in the given - /// `VecDeque`. The collection may reserve more space to avoid frequent reallocations. - /// - /// # Panics - /// - /// Panics if the new capacity overflows `usize`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf: VecDeque = vec![1].into_iter().collect(); - /// buf.reserve(10); - /// assert!(buf.capacity() >= 11); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn reserve(&mut self, additional: usize) { - let old_cap = self.cap(); - let used_cap = self.len() + 1; - let new_cap = used_cap.checked_add(additional) - .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) - .expect("capacity overflow"); - - if new_cap > old_cap { - self.buf.reserve_exact(used_cap, new_cap - used_cap); - unsafe { - self.handle_cap_increase(old_cap); - } - } - } - - /// Tries to reserves the minimum capacity for exactly `additional` more elements to - /// be inserted in the given `VecDeque`. After calling `reserve_exact`, - /// capacity will be greater than or equal to `self.len() + additional`. - /// Does nothing if the capacity is already sufficient. - /// - /// Note that the allocator may give the collection more space than it - /// requests. Therefore capacity can not be relied upon to be precisely - /// minimal. Prefer `reserve` if future insertions are expected. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::CollectionAllocErr; - /// use std::collections::VecDeque; - /// - /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { - /// let mut output = VecDeque::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve_exact(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.extend(data.iter().map(|&val| { - /// val * 2 + 5 // very complicated - /// })); - /// - /// Ok(output) - /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] - pub fn try_reserve_exact(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { - self.try_reserve(additional) - } - - /// Tries to reserve capacity for at least `additional` more elements to be inserted - /// in the given `VecDeque`. The collection may reserve more space to avoid - /// frequent reallocations. After calling `reserve`, capacity will be - /// greater than or equal to `self.len() + additional`. Does nothing if - /// capacity is already sufficient. - /// - /// # Errors - /// - /// If the capacity overflows, or the allocator reports a failure, then an error - /// is returned. - /// - /// # Examples - /// - /// ``` - /// #![feature(try_reserve)] - /// use std::collections::CollectionAllocErr; - /// use std::collections::VecDeque; - /// - /// fn process_data(data: &[u32]) -> Result, CollectionAllocErr> { - /// let mut output = VecDeque::new(); - /// - /// // Pre-reserve the memory, exiting if we can't - /// output.try_reserve(data.len())?; - /// - /// // Now we know this can't OOM in the middle of our complex work - /// output.extend(data.iter().map(|&val| { - /// val * 2 + 5 // very complicated - /// })); - /// - /// Ok(output) - /// } - /// # process_data(&[1, 2, 3]).expect("why is the test harness OOMing on 12 bytes?"); - /// ``` - #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] - pub fn try_reserve(&mut self, additional: usize) -> Result<(), CollectionAllocErr> { - let old_cap = self.cap(); - let used_cap = self.len() + 1; - let new_cap = used_cap.checked_add(additional) - .and_then(|needed_cap| needed_cap.checked_next_power_of_two()) - .ok_or(CollectionAllocErr::CapacityOverflow)?; - - if new_cap > old_cap { - self.buf.try_reserve_exact(used_cap, new_cap - used_cap)?; - unsafe { - self.handle_cap_increase(old_cap); - } - } - Ok(()) - } - - /// Shrinks the capacity of the `VecDeque` as much as possible. - /// - /// It will drop down as close as possible to the length but the allocator may still inform the - /// `VecDeque` that there is space for a few more elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::with_capacity(15); - /// buf.extend(0..4); - /// assert_eq!(buf.capacity(), 15); - /// buf.shrink_to_fit(); - /// assert!(buf.capacity() >= 4); - /// ``` - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn shrink_to_fit(&mut self) { - self.shrink_to(0); - } - - /// Shrinks the capacity of the `VecDeque` with a lower bound. - /// - /// The capacity will remain at least as large as both the length - /// and the supplied value. - /// - /// Panics if the current capacity is smaller than the supplied - /// minimum capacity. - /// - /// # Examples - /// - /// ``` - /// #![feature(shrink_to)] - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::with_capacity(15); - /// buf.extend(0..4); - /// assert_eq!(buf.capacity(), 15); - /// buf.shrink_to(6); - /// assert!(buf.capacity() >= 6); - /// buf.shrink_to(0); - /// assert!(buf.capacity() >= 4); - /// ``` - #[unstable(feature = "shrink_to", reason = "new API", issue="0")] - pub fn shrink_to(&mut self, min_capacity: usize) { - assert!(self.capacity() >= min_capacity, "Tried to shrink to a larger capacity"); - - // +1 since the ringbuffer always leaves one space empty - // len + 1 can't overflow for an existing, well-formed ringbuffer. - let target_cap = cmp::max( - cmp::max(min_capacity, self.len()) + 1, - MINIMUM_CAPACITY + 1 - ).next_power_of_two(); - - if target_cap < self.cap() { - // There are three cases of interest: - // All elements are out of desired bounds - // Elements are contiguous, and head is out of desired bounds - // Elements are discontiguous, and tail is out of desired bounds - // - // At all other times, element positions are unaffected. - // - // Indicates that elements at the head should be moved. - let head_outside = self.head == 0 || self.head >= target_cap; - // Move elements from out of desired bounds (positions after target_cap) - if self.tail >= target_cap && head_outside { - // T H - // [. . . . . . . . o o o o o o o . ] - // T H - // [o o o o o o o . ] - unsafe { - self.copy_nonoverlapping(0, self.tail, self.len()); - } - self.head = self.len(); - self.tail = 0; - } else if self.tail != 0 && self.tail < target_cap && head_outside { - // T H - // [. . . o o o o o o o . . . . . . ] - // H T - // [o o . o o o o o ] - let len = self.wrap_sub(self.head, target_cap); - unsafe { - self.copy_nonoverlapping(0, target_cap, len); - } - self.head = len; - debug_assert!(self.head < self.tail); - } else if self.tail >= target_cap { - // H T - // [o o o o o . . . . . . . . . o o ] - // H T - // [o o o o o . o o ] - debug_assert!(self.wrap_sub(self.head, 1) < target_cap); - let len = self.cap() - self.tail; - let new_tail = target_cap - len; - unsafe { - self.copy_nonoverlapping(new_tail, self.tail, len); - } - self.tail = new_tail; - debug_assert!(self.head < self.tail); - } - - self.buf.shrink_to_fit(target_cap); - - debug_assert!(self.head < self.cap()); - debug_assert!(self.tail < self.cap()); - debug_assert!(self.cap().count_ones() == 1); - } - } - - /// Shortens the `VecDeque`, dropping excess elements from the back. - /// - /// If `len` is greater than the `VecDeque`'s current length, this has no - /// effect. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(5); - /// buf.push_back(10); - /// buf.push_back(15); - /// assert_eq!(buf, [5, 10, 15]); - /// buf.truncate(1); - /// assert_eq!(buf, [5]); - /// ``` - #[stable(feature = "deque_extras", since = "1.16.0")] - pub fn truncate(&mut self, len: usize) { - for _ in len..self.len() { - self.pop_back(); - } - } - - /// Returns a front-to-back iterator. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(5); - /// buf.push_back(3); - /// buf.push_back(4); - /// let b: &[_] = &[&5, &3, &4]; - /// let c: Vec<&i32> = buf.iter().collect(); - /// assert_eq!(&c[..], b); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter(&self) -> Iter { - Iter { - tail: self.tail, - head: self.head, - ring: unsafe { self.buffer_as_slice() }, - } - } - - /// Returns a front-to-back iterator that returns mutable references. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(5); - /// buf.push_back(3); - /// buf.push_back(4); - /// for num in buf.iter_mut() { - /// *num = *num - 2; - /// } - /// let b: &[_] = &[&mut 3, &mut 1, &mut 2]; - /// assert_eq!(&buf.iter_mut().collect::>()[..], b); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn iter_mut(&mut self) -> IterMut { - IterMut { - tail: self.tail, - head: self.head, - ring: unsafe { self.buffer_as_mut_slice() }, - } - } - - /// Returns a pair of slices which contain, in order, the contents of the - /// `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut vector = VecDeque::new(); - /// - /// vector.push_back(0); - /// vector.push_back(1); - /// vector.push_back(2); - /// - /// assert_eq!(vector.as_slices(), (&[0, 1, 2][..], &[][..])); - /// - /// vector.push_front(10); - /// vector.push_front(9); - /// - /// assert_eq!(vector.as_slices(), (&[9, 10][..], &[0, 1, 2][..])); - /// ``` - #[inline] - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn as_slices(&self) -> (&[T], &[T]) { - unsafe { - let buf = self.buffer_as_slice(); - RingSlices::ring_slices(buf, self.head, self.tail) - } - } - - /// Returns a pair of slices which contain, in order, the contents of the - /// `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut vector = VecDeque::new(); - /// - /// vector.push_back(0); - /// vector.push_back(1); - /// - /// vector.push_front(10); - /// vector.push_front(9); - /// - /// vector.as_mut_slices().0[0] = 42; - /// vector.as_mut_slices().1[0] = 24; - /// assert_eq!(vector.as_slices(), (&[42, 10][..], &[24, 1][..])); - /// ``` - #[inline] - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) { - unsafe { - let head = self.head; - let tail = self.tail; - let buf = self.buffer_as_mut_slice(); - RingSlices::ring_slices(buf, head, tail) - } - } - - /// Returns the number of elements in the `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut v = VecDeque::new(); - /// assert_eq!(v.len(), 0); - /// v.push_back(1); - /// assert_eq!(v.len(), 1); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn len(&self) -> usize { - count(self.tail, self.head, self.cap()) - } - - /// Returns `true` if the `VecDeque` is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut v = VecDeque::new(); - /// assert!(v.is_empty()); - /// v.push_front(1); - /// assert!(!v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn is_empty(&self) -> bool { - self.tail == self.head - } - - /// Create a draining iterator that removes the specified range in the - /// `VecDeque` and yields the removed items. - /// - /// Note 1: The element range is removed even if the iterator is not - /// consumed until the end. - /// - /// Note 2: It is unspecified how many elements are removed from the deque, - /// if the `Drain` value is not dropped, but the borrow it holds expires - /// (eg. due to mem::forget). - /// - /// # Panics - /// - /// Panics if the starting point is greater than the end point or if - /// the end point is greater than the length of the vector. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect(); - /// let drained = v.drain(2..).collect::>(); - /// assert_eq!(drained, [3]); - /// assert_eq!(v, [1, 2]); - /// - /// // A full range clears all contents - /// v.drain(..); - /// assert!(v.is_empty()); - /// ``` - #[inline] - #[stable(feature = "drain", since = "1.6.0")] - pub fn drain(&mut self, range: R) -> Drain - where R: RangeBounds - { - // Memory safety - // - // When the Drain is first created, the source deque is shortened to - // make sure no uninitialized or moved-from elements are accessible at - // all if the Drain's destructor never gets to run. - // - // Drain will ptr::read out the values to remove. - // When finished, the remaining data will be copied back to cover the hole, - // and the head/tail values will be restored correctly. - // - let len = self.len(); - let start = match range.start_bound() { - Included(&n) => n, - Excluded(&n) => n + 1, - Unbounded => 0, - }; - let end = match range.end_bound() { - Included(&n) => n + 1, - Excluded(&n) => n, - Unbounded => len, - }; - assert!(start <= end, "drain lower bound was too large"); - assert!(end <= len, "drain upper bound was too large"); - - // The deque's elements are parted into three segments: - // * self.tail -> drain_tail - // * drain_tail -> drain_head - // * drain_head -> self.head - // - // T = self.tail; H = self.head; t = drain_tail; h = drain_head - // - // We store drain_tail as self.head, and drain_head and self.head as - // after_tail and after_head respectively on the Drain. This also - // truncates the effective array such that if the Drain is leaked, we - // have forgotten about the potentially moved values after the start of - // the drain. - // - // T t h H - // [. . . o o x x o o . . .] - // - let drain_tail = self.wrap_add(self.tail, start); - let drain_head = self.wrap_add(self.tail, end); - let head = self.head; - - // "forget" about the values after the start of the drain until after - // the drain is complete and the Drain destructor is run. - self.head = drain_tail; - - Drain { - deque: NonNull::from(&mut *self), - after_tail: drain_head, - after_head: head, - iter: Iter { - tail: drain_tail, - head: drain_head, - ring: unsafe { self.buffer_as_mut_slice() }, - }, - } - } - - /// Clears the `VecDeque`, removing all values. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut v = VecDeque::new(); - /// v.push_back(1); - /// v.clear(); - /// assert!(v.is_empty()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - #[inline] - pub fn clear(&mut self) { - self.drain(..); - } - - /// Returns `true` if the `VecDeque` contains an element equal to the - /// given value. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut vector: VecDeque = VecDeque::new(); - /// - /// vector.push_back(0); - /// vector.push_back(1); - /// - /// assert_eq!(vector.contains(&1), true); - /// assert_eq!(vector.contains(&10), false); - /// ``` - #[stable(feature = "vec_deque_contains", since = "1.12.0")] - pub fn contains(&self, x: &T) -> bool - where T: PartialEq - { - let (a, b) = self.as_slices(); - a.contains(x) || b.contains(x) - } - - /// Provides a reference to the front element, or `None` if the `VecDeque` is - /// empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// assert_eq!(d.front(), None); - /// - /// d.push_back(1); - /// d.push_back(2); - /// assert_eq!(d.front(), Some(&1)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn front(&self) -> Option<&T> { - if !self.is_empty() { - Some(&self[0]) - } else { - None - } - } - - /// Provides a mutable reference to the front element, or `None` if the - /// `VecDeque` is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// assert_eq!(d.front_mut(), None); - /// - /// d.push_back(1); - /// d.push_back(2); - /// match d.front_mut() { - /// Some(x) => *x = 9, - /// None => (), - /// } - /// assert_eq!(d.front(), Some(&9)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn front_mut(&mut self) -> Option<&mut T> { - if !self.is_empty() { - Some(&mut self[0]) - } else { - None - } - } - - /// Provides a reference to the back element, or `None` if the `VecDeque` is - /// empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// assert_eq!(d.back(), None); - /// - /// d.push_back(1); - /// d.push_back(2); - /// assert_eq!(d.back(), Some(&2)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn back(&self) -> Option<&T> { - if !self.is_empty() { - Some(&self[self.len() - 1]) - } else { - None - } - } - - /// Provides a mutable reference to the back element, or `None` if the - /// `VecDeque` is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// assert_eq!(d.back(), None); - /// - /// d.push_back(1); - /// d.push_back(2); - /// match d.back_mut() { - /// Some(x) => *x = 9, - /// None => (), - /// } - /// assert_eq!(d.back(), Some(&9)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn back_mut(&mut self) -> Option<&mut T> { - let len = self.len(); - if !self.is_empty() { - Some(&mut self[len - 1]) - } else { - None - } - } - - /// Removes the first element and returns it, or `None` if the `VecDeque` is - /// empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// d.push_back(1); - /// d.push_back(2); - /// - /// assert_eq!(d.pop_front(), Some(1)); - /// assert_eq!(d.pop_front(), Some(2)); - /// assert_eq!(d.pop_front(), None); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop_front(&mut self) -> Option { - if self.is_empty() { - None - } else { - let tail = self.tail; - self.tail = self.wrap_add(self.tail, 1); - unsafe { Some(self.buffer_read(tail)) } - } - } - - /// Prepends an element to the `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut d = VecDeque::new(); - /// d.push_front(1); - /// d.push_front(2); - /// assert_eq!(d.front(), Some(&2)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push_front(&mut self, value: T) { - self.grow_if_necessary(); - - self.tail = self.wrap_sub(self.tail, 1); - let tail = self.tail; - unsafe { - self.buffer_write(tail, value); - } - } - - /// Appends an element to the back of the `VecDeque`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(1); - /// buf.push_back(3); - /// assert_eq!(3, *buf.back().unwrap()); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn push_back(&mut self, value: T) { - self.grow_if_necessary(); - - let head = self.head; - self.head = self.wrap_add(self.head, 1); - unsafe { self.buffer_write(head, value) } - } - - /// Removes the last element from the `VecDeque` and returns it, or `None` if - /// it is empty. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// assert_eq!(buf.pop_back(), None); - /// buf.push_back(1); - /// buf.push_back(3); - /// assert_eq!(buf.pop_back(), Some(3)); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn pop_back(&mut self) -> Option { - if self.is_empty() { - None - } else { - self.head = self.wrap_sub(self.head, 1); - let head = self.head; - unsafe { Some(self.buffer_read(head)) } - } - } - - #[inline] - fn is_contiguous(&self) -> bool { - self.tail <= self.head - } - - /// Removes an element from anywhere in the `VecDeque` and returns it, replacing it with the - /// last element. - /// - /// This does not preserve ordering, but is O(1). - /// - /// Returns `None` if `index` is out of bounds. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// assert_eq!(buf.swap_remove_back(0), None); - /// buf.push_back(1); - /// buf.push_back(2); - /// buf.push_back(3); - /// assert_eq!(buf, [1, 2, 3]); - /// - /// assert_eq!(buf.swap_remove_back(0), Some(1)); - /// assert_eq!(buf, [3, 2]); - /// ``` - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn swap_remove_back(&mut self, index: usize) -> Option { - let length = self.len(); - if length > 0 && index < length - 1 { - self.swap(index, length - 1); - } else if index >= length { - return None; - } - self.pop_back() - } - - /// Removes an element from anywhere in the `VecDeque` and returns it, - /// replacing it with the first element. - /// - /// This does not preserve ordering, but is O(1). - /// - /// Returns `None` if `index` is out of bounds. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// assert_eq!(buf.swap_remove_front(0), None); - /// buf.push_back(1); - /// buf.push_back(2); - /// buf.push_back(3); - /// assert_eq!(buf, [1, 2, 3]); - /// - /// assert_eq!(buf.swap_remove_front(2), Some(3)); - /// assert_eq!(buf, [2, 1]); - /// ``` - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn swap_remove_front(&mut self, index: usize) -> Option { - let length = self.len(); - if length > 0 && index < length && index != 0 { - self.swap(index, 0); - } else if index >= length { - return None; - } - self.pop_front() - } - - /// Inserts an element at `index` within the `VecDeque`, shifting all elements with indices - /// greater than or equal to `index` towards the back. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Panics - /// - /// Panics if `index` is greater than `VecDeque`'s length - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut vec_deque = VecDeque::new(); - /// vec_deque.push_back('a'); - /// vec_deque.push_back('b'); - /// vec_deque.push_back('c'); - /// assert_eq!(vec_deque, &['a', 'b', 'c']); - /// - /// vec_deque.insert(1, 'd'); - /// assert_eq!(vec_deque, &['a', 'd', 'b', 'c']); - /// ``` - #[stable(feature = "deque_extras_15", since = "1.5.0")] - pub fn insert(&mut self, index: usize, value: T) { - assert!(index <= self.len(), "index out of bounds"); - self.grow_if_necessary(); - - // Move the least number of elements in the ring buffer and insert - // the given object - // - // At most len/2 - 1 elements will be moved. O(min(n, n-i)) - // - // There are three main cases: - // Elements are contiguous - // - special case when tail is 0 - // Elements are discontiguous and the insert is in the tail section - // Elements are discontiguous and the insert is in the head section - // - // For each of those there are two more cases: - // Insert is closer to tail - // Insert is closer to head - // - // Key: H - self.head - // T - self.tail - // o - Valid element - // I - Insertion element - // A - The element that should be after the insertion point - // M - Indicates element was moved - - let idx = self.wrap_add(self.tail, index); - - let distance_to_tail = index; - let distance_to_head = self.len() - index; - - let contiguous = self.is_contiguous(); - - match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { - (true, true, _) if index == 0 => { - // push_front - // - // T - // I H - // [A o o o o o o . . . . . . . . .] - // - // H T - // [A o o o o o o o . . . . . I] - // - - self.tail = self.wrap_sub(self.tail, 1); - } - (true, true, _) => { - unsafe { - // contiguous, insert closer to tail: - // - // T I H - // [. . . o o A o o o o . . . . . .] - // - // T H - // [. . o o I A o o o o . . . . . .] - // M M - // - // contiguous, insert closer to tail and tail is 0: - // - // - // T I H - // [o o A o o o o . . . . . . . . .] - // - // H T - // [o I A o o o o o . . . . . . . o] - // M M - - let new_tail = self.wrap_sub(self.tail, 1); - - self.copy(new_tail, self.tail, 1); - // Already moved the tail, so we only copy `index - 1` elements. - self.copy(self.tail, self.tail + 1, index - 1); - - self.tail = new_tail; - } - } - (true, false, _) => { - unsafe { - // contiguous, insert closer to head: - // - // T I H - // [. . . o o o o A o o . . . . . .] - // - // T H - // [. . . o o o o I A o o . . . . .] - // M M M - - self.copy(idx + 1, idx, self.head - idx); - self.head = self.wrap_add(self.head, 1); - } - } - (false, true, true) => { - unsafe { - // discontiguous, insert closer to tail, tail section: - // - // H T I - // [o o o o o o . . . . . o o A o o] - // - // H T - // [o o o o o o . . . . o o I A o o] - // M M - - self.copy(self.tail - 1, self.tail, index); - self.tail -= 1; - } - } - (false, false, true) => { - unsafe { - // discontiguous, insert closer to head, tail section: - // - // H T I - // [o o . . . . . . . o o o o o A o] - // - // H T - // [o o o . . . . . . o o o o o I A] - // M M M M - - // copy elements up to new head - self.copy(1, 0, self.head); - - // copy last element into empty spot at bottom of buffer - self.copy(0, self.cap() - 1, 1); - - // move elements from idx to end forward not including ^ element - self.copy(idx + 1, idx, self.cap() - 1 - idx); - - self.head += 1; - } - } - (false, true, false) if idx == 0 => { - unsafe { - // discontiguous, insert is closer to tail, head section, - // and is at index zero in the internal buffer: - // - // I H T - // [A o o o o o o o o o . . . o o o] - // - // H T - // [A o o o o o o o o o . . o o o I] - // M M M - - // copy elements up to new tail - self.copy(self.tail - 1, self.tail, self.cap() - self.tail); - - // copy last element into empty spot at bottom of buffer - self.copy(self.cap() - 1, 0, 1); - - self.tail -= 1; - } - } - (false, true, false) => { - unsafe { - // discontiguous, insert closer to tail, head section: - // - // I H T - // [o o o A o o o o o o . . . o o o] - // - // H T - // [o o I A o o o o o o . . o o o o] - // M M M M M M - - // copy elements up to new tail - self.copy(self.tail - 1, self.tail, self.cap() - self.tail); - - // copy last element into empty spot at bottom of buffer - self.copy(self.cap() - 1, 0, 1); - - // move elements from idx-1 to end forward not including ^ element - self.copy(0, 1, idx - 1); - - self.tail -= 1; - } - } - (false, false, false) => { - unsafe { - // discontiguous, insert closer to head, head section: - // - // I H T - // [o o o o A o o . . . . . . o o o] - // - // H T - // [o o o o I A o o . . . . . o o o] - // M M M - - self.copy(idx + 1, idx, self.head - idx); - self.head += 1; - } - } - } - - // tail might've been changed so we need to recalculate - let new_idx = self.wrap_add(self.tail, index); - unsafe { - self.buffer_write(new_idx, value); - } - } - - /// Removes and returns the element at `index` from the `VecDeque`. - /// Whichever end is closer to the removal point will be moved to make - /// room, and all the affected elements will be moved to new positions. - /// Returns `None` if `index` is out of bounds. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(1); - /// buf.push_back(2); - /// buf.push_back(3); - /// assert_eq!(buf, [1, 2, 3]); - /// - /// assert_eq!(buf.remove(1), Some(2)); - /// assert_eq!(buf, [1, 3]); - /// ``` - #[stable(feature = "rust1", since = "1.0.0")] - pub fn remove(&mut self, index: usize) -> Option { - if self.is_empty() || self.len() <= index { - return None; - } - - // There are three main cases: - // Elements are contiguous - // Elements are discontiguous and the removal is in the tail section - // Elements are discontiguous and the removal is in the head section - // - special case when elements are technically contiguous, - // but self.head = 0 - // - // For each of those there are two more cases: - // Insert is closer to tail - // Insert is closer to head - // - // Key: H - self.head - // T - self.tail - // o - Valid element - // x - Element marked for removal - // R - Indicates element that is being removed - // M - Indicates element was moved - - let idx = self.wrap_add(self.tail, index); - - let elem = unsafe { Some(self.buffer_read(idx)) }; - - let distance_to_tail = index; - let distance_to_head = self.len() - index; - - let contiguous = self.is_contiguous(); - - match (contiguous, distance_to_tail <= distance_to_head, idx >= self.tail) { - (true, true, _) => { - unsafe { - // contiguous, remove closer to tail: - // - // T R H - // [. . . o o x o o o o . . . . . .] - // - // T H - // [. . . . o o o o o o . . . . . .] - // M M - - self.copy(self.tail + 1, self.tail, index); - self.tail += 1; - } - } - (true, false, _) => { - unsafe { - // contiguous, remove closer to head: - // - // T R H - // [. . . o o o o x o o . . . . . .] - // - // T H - // [. . . o o o o o o . . . . . . .] - // M M - - self.copy(idx, idx + 1, self.head - idx - 1); - self.head -= 1; - } - } - (false, true, true) => { - unsafe { - // discontiguous, remove closer to tail, tail section: - // - // H T R - // [o o o o o o . . . . . o o x o o] - // - // H T - // [o o o o o o . . . . . . o o o o] - // M M - - self.copy(self.tail + 1, self.tail, index); - self.tail = self.wrap_add(self.tail, 1); - } - } - (false, false, false) => { - unsafe { - // discontiguous, remove closer to head, head section: - // - // R H T - // [o o o o x o o . . . . . . o o o] - // - // H T - // [o o o o o o . . . . . . . o o o] - // M M - - self.copy(idx, idx + 1, self.head - idx - 1); - self.head -= 1; - } - } - (false, false, true) => { - unsafe { - // discontiguous, remove closer to head, tail section: - // - // H T R - // [o o o . . . . . . o o o o o x o] - // - // H T - // [o o . . . . . . . o o o o o o o] - // M M M M - // - // or quasi-discontiguous, remove next to head, tail section: - // - // H T R - // [. . . . . . . . . o o o o o x o] - // - // T H - // [. . . . . . . . . o o o o o o .] - // M - - // draw in elements in the tail section - self.copy(idx, idx + 1, self.cap() - idx - 1); - - // Prevents underflow. - if self.head != 0 { - // copy first element into empty spot - self.copy(self.cap() - 1, 0, 1); - - // move elements in the head section backwards - self.copy(0, 1, self.head - 1); - } - - self.head = self.wrap_sub(self.head, 1); - } - } - (false, true, false) => { - unsafe { - // discontiguous, remove closer to tail, head section: - // - // R H T - // [o o x o o o o o o o . . . o o o] - // - // H T - // [o o o o o o o o o o . . . . o o] - // M M M M M - - // draw in elements up to idx - self.copy(1, 0, idx); - - // copy last element into empty spot - self.copy(0, self.cap() - 1, 1); - - // move elements from tail to end forward, excluding the last one - self.copy(self.tail + 1, self.tail, self.cap() - self.tail - 1); - - self.tail = self.wrap_add(self.tail, 1); - } - } - } - - return elem; - } - - /// Splits the `VecDeque` into two at the given index. - /// - /// Returns a newly allocated `VecDeque`. `self` contains elements `[0, at)`, - /// and the returned `VecDeque` contains elements `[at, len)`. - /// - /// Note that the capacity of `self` does not change. - /// - /// Element at index 0 is the front of the queue. - /// - /// # Panics - /// - /// Panics if `at > len`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf: VecDeque<_> = vec![1,2,3].into_iter().collect(); - /// let buf2 = buf.split_off(1); - /// assert_eq!(buf, [1]); - /// assert_eq!(buf2, [2, 3]); - /// ``` - #[inline] - #[stable(feature = "split_off", since = "1.4.0")] - pub fn split_off(&mut self, at: usize) -> Self { - let len = self.len(); - assert!(at <= len, "`at` out of bounds"); - - let other_len = len - at; - let mut other = VecDeque::with_capacity(other_len); - - unsafe { - let (first_half, second_half) = self.as_slices(); - - let first_len = first_half.len(); - let second_len = second_half.len(); - if at < first_len { - // `at` lies in the first half. - let amount_in_first = first_len - at; - - ptr::copy_nonoverlapping(first_half.as_ptr().offset(at as isize), - other.ptr(), - amount_in_first); - - // just take all of the second half. - ptr::copy_nonoverlapping(second_half.as_ptr(), - other.ptr().offset(amount_in_first as isize), - second_len); - } else { - // `at` lies in the second half, need to factor in the elements we skipped - // in the first half. - let offset = at - first_len; - let amount_in_second = second_len - offset; - ptr::copy_nonoverlapping(second_half.as_ptr().offset(offset as isize), - other.ptr(), - amount_in_second); - } - } - - // Cleanup where the ends of the buffers are - self.head = self.wrap_sub(self.head, other_len); - other.head = other.wrap_index(other_len); - - other - } - - /// Moves all the elements of `other` into `Self`, leaving `other` empty. - /// - /// # Panics - /// - /// Panics if the new number of elements in self overflows a `usize`. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf: VecDeque<_> = vec![1, 2].into_iter().collect(); - /// let mut buf2: VecDeque<_> = vec![3, 4].into_iter().collect(); - /// buf.append(&mut buf2); - /// assert_eq!(buf, [1, 2, 3, 4]); - /// assert_eq!(buf2, []); - /// ``` - #[inline] - #[stable(feature = "append", since = "1.4.0")] - pub fn append(&mut self, other: &mut Self) { - // naive impl - self.extend(other.drain(..)); - } - - /// Retains only the elements specified by the predicate. - /// - /// In other words, remove all elements `e` such that `f(&e)` returns false. - /// This method operates in place and preserves the order of the retained - /// elements. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.extend(1..5); - /// buf.retain(|&x| x%2 == 0); - /// assert_eq!(buf, [2, 4]); - /// ``` - #[stable(feature = "vec_deque_retain", since = "1.4.0")] - pub fn retain(&mut self, mut f: F) - where F: FnMut(&T) -> bool - { - let len = self.len(); - let mut del = 0; - for i in 0..len { - if !f(&self[i]) { - del += 1; - } else if del > 0 { - self.swap(i - del, i); - } - } - if del > 0 { - self.truncate(len - del); - } - } - - // This may panic or abort - #[inline] - fn grow_if_necessary(&mut self) { - if self.is_full() { - let old_cap = self.cap(); - self.buf.double(); - unsafe { - self.handle_cap_increase(old_cap); - } - debug_assert!(!self.is_full()); - } - } -} - -impl VecDeque { - /// Modifies the `VecDeque` in-place so that `len()` is equal to new_len, - /// either by removing excess elements from the back or by appending clones of `value` - /// to the back. - /// - /// # Examples - /// - /// ``` - /// use std::collections::VecDeque; - /// - /// let mut buf = VecDeque::new(); - /// buf.push_back(5); - /// buf.push_back(10); - /// buf.push_back(15); - /// assert_eq!(buf, [5, 10, 15]); - /// - /// buf.resize(2, 0); - /// assert_eq!(buf, [5, 10]); - /// - /// buf.resize(5, 20); - /// assert_eq!(buf, [5, 10, 20, 20, 20]); - /// ``` - #[stable(feature = "deque_extras", since = "1.16.0")] - pub fn resize(&mut self, new_len: usize, value: T) { - let len = self.len(); - - if new_len > len { - self.extend(repeat(value).take(new_len - len)) - } else { - self.truncate(new_len); - } - } -} - -/// Returns the index in the underlying buffer for a given logical element index. -#[inline] -fn wrap_index(index: usize, size: usize) -> usize { - // size is always a power of 2 - debug_assert!(size.is_power_of_two()); - index & (size - 1) -} - -/// Returns the two slices that cover the `VecDeque`'s valid range -trait RingSlices: Sized { - fn slice(self, from: usize, to: usize) -> Self; - fn split_at(self, i: usize) -> (Self, Self); - - fn ring_slices(buf: Self, head: usize, tail: usize) -> (Self, Self) { - let contiguous = tail <= head; - if contiguous { - let (empty, buf) = buf.split_at(0); - (buf.slice(tail, head), empty) - } else { - let (mid, right) = buf.split_at(tail); - let (left, _) = mid.split_at(head); - (right, left) - } - } -} - -impl<'a, T> RingSlices for &'a [T] { - fn slice(self, from: usize, to: usize) -> Self { - &self[from..to] - } - fn split_at(self, i: usize) -> (Self, Self) { - (*self).split_at(i) - } -} - -impl<'a, T> RingSlices for &'a mut [T] { - fn slice(self, from: usize, to: usize) -> Self { - &mut self[from..to] - } - fn split_at(self, i: usize) -> (Self, Self) { - (*self).split_at_mut(i) - } -} - -/// Calculate the number of elements left to be read in the buffer -#[inline] -fn count(tail: usize, head: usize, size: usize) -> usize { - // size is always a power of 2 - (head.wrapping_sub(tail)) & (size - 1) -} - -/// An iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`iter`] method on [`VecDeque`]. See its -/// documentation for more. -/// -/// [`iter`]: struct.VecDeque.html#method.iter -/// [`VecDeque`]: struct.VecDeque.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Iter<'a, T: 'a> { - ring: &'a [T], - tail: usize, - head: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Iter<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Iter") - .field(&self.ring) - .field(&self.tail) - .field(&self.head) - .finish() - } -} - -// FIXME(#26925) Remove in favor of `#[derive(Clone)]` -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Clone for Iter<'a, T> { - fn clone(&self) -> Iter<'a, T> { - Iter { - ring: self.ring, - tail: self.tail, - head: self.head, - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for Iter<'a, T> { - type Item = &'a T; - - #[inline] - fn next(&mut self) -> Option<&'a T> { - if self.tail == self.head { - return None; - } - let tail = self.tail; - self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(tail)) } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = count(self.tail, self.head, self.ring.len()); - (len, Some(len)) - } - - fn fold(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = front.iter().fold(accum, &mut f); - back.iter().fold(accum, &mut f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for Iter<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a T> { - if self.tail == self.head { - return None; - } - self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); - unsafe { Some(self.ring.get_unchecked(self.head)) } - } - - fn rfold(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = back.iter().rfold(accum, &mut f); - front.iter().rfold(accum, &mut f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for Iter<'a, T> { - fn is_empty(&self) -> bool { - self.head == self.tail - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for Iter<'a, T> {} - - -/// A mutable iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`iter_mut`] method on [`VecDeque`]. See its -/// documentation for more. -/// -/// [`iter_mut`]: struct.VecDeque.html#method.iter_mut -/// [`VecDeque`]: struct.VecDeque.html -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IterMut<'a, T: 'a> { - ring: &'a mut [T], - tail: usize, - head: usize, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for IterMut<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("IterMut") - .field(&self.ring) - .field(&self.tail) - .field(&self.head) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> Iterator for IterMut<'a, T> { - type Item = &'a mut T; - - #[inline] - fn next(&mut self) -> Option<&'a mut T> { - if self.tail == self.head { - return None; - } - let tail = self.tail; - self.tail = wrap_index(self.tail.wrapping_add(1), self.ring.len()); - - unsafe { - let elem = self.ring.get_unchecked_mut(tail); - Some(&mut *(elem as *mut _)) - } - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = count(self.tail, self.head, self.ring.len()); - (len, Some(len)) - } - - fn fold(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = front.iter_mut().fold(accum, &mut f); - back.iter_mut().fold(accum, &mut f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> DoubleEndedIterator for IterMut<'a, T> { - #[inline] - fn next_back(&mut self) -> Option<&'a mut T> { - if self.tail == self.head { - return None; - } - self.head = wrap_index(self.head.wrapping_sub(1), self.ring.len()); - - unsafe { - let elem = self.ring.get_unchecked_mut(self.head); - Some(&mut *(elem as *mut _)) - } - } - - fn rfold(self, mut accum: Acc, mut f: F) -> Acc - where F: FnMut(Acc, Self::Item) -> Acc - { - let (front, back) = RingSlices::ring_slices(self.ring, self.head, self.tail); - accum = back.iter_mut().rfold(accum, &mut f); - front.iter_mut().rfold(accum, &mut f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> ExactSizeIterator for IterMut<'a, T> { - fn is_empty(&self) -> bool { - self.head == self.tail - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T> FusedIterator for IterMut<'a, T> {} - -/// An owning iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`into_iter`] method on [`VecDeque`][`VecDeque`] -/// (provided by the `IntoIterator` trait). See its documentation for more. -/// -/// [`into_iter`]: struct.VecDeque.html#method.into_iter -/// [`VecDeque`]: struct.VecDeque.html -#[derive(Clone)] -#[stable(feature = "rust1", since = "1.0.0")] -pub struct IntoIter { - inner: VecDeque, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl fmt::Debug for IntoIter { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("IntoIter") - .field(&self.inner) - .finish() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Iterator for IntoIter { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.inner.pop_front() - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - let len = self.inner.len(); - (len, Some(len)) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl DoubleEndedIterator for IntoIter { - #[inline] - fn next_back(&mut self) -> Option { - self.inner.pop_back() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl ExactSizeIterator for IntoIter { - fn is_empty(&self) -> bool { - self.inner.is_empty() - } -} - -#[stable(feature = "fused", since = "1.26.0")] -impl FusedIterator for IntoIter {} - -/// A draining iterator over the elements of a `VecDeque`. -/// -/// This `struct` is created by the [`drain`] method on [`VecDeque`]. See its -/// documentation for more. -/// -/// [`drain`]: struct.VecDeque.html#method.drain -/// [`VecDeque`]: struct.VecDeque.html -#[stable(feature = "drain", since = "1.6.0")] -pub struct Drain<'a, T: 'a> { - after_tail: usize, - after_head: usize, - iter: Iter<'a, T>, - deque: NonNull>, -} - -#[stable(feature = "collection_debug", since = "1.17.0")] -impl<'a, T: 'a + fmt::Debug> fmt::Debug for Drain<'a, T> { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_tuple("Drain") - .field(&self.after_tail) - .field(&self.after_head) - .field(&self.iter) - .finish() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl<'a, T: Sync> Sync for Drain<'a, T> {} -#[stable(feature = "drain", since = "1.6.0")] -unsafe impl<'a, T: Send> Send for Drain<'a, T> {} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> Drop for Drain<'a, T> { - fn drop(&mut self) { - self.for_each(drop); - - let source_deque = unsafe { self.deque.as_mut() }; - - // T = source_deque_tail; H = source_deque_head; t = drain_tail; h = drain_head - // - // T t h H - // [. . . o o x x o o . . .] - // - let orig_tail = source_deque.tail; - let drain_tail = source_deque.head; - let drain_head = self.after_tail; - let orig_head = self.after_head; - - let tail_len = count(orig_tail, drain_tail, source_deque.cap()); - let head_len = count(drain_head, orig_head, source_deque.cap()); - - // Restore the original head value - source_deque.head = orig_head; - - match (tail_len, head_len) { - (0, 0) => { - source_deque.head = 0; - source_deque.tail = 0; - } - (0, _) => { - source_deque.tail = drain_head; - } - (_, 0) => { - source_deque.head = drain_tail; - } - _ => unsafe { - if tail_len <= head_len { - source_deque.tail = source_deque.wrap_sub(drain_head, tail_len); - source_deque.wrap_copy(source_deque.tail, orig_tail, tail_len); - } else { - source_deque.head = source_deque.wrap_add(drain_tail, head_len); - source_deque.wrap_copy(drain_tail, drain_head, head_len); - } - }, - } - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> Iterator for Drain<'a, T> { - type Item = T; - - #[inline] - fn next(&mut self) -> Option { - self.iter.next().map(|elt| unsafe { ptr::read(elt) }) - } - - #[inline] - fn size_hint(&self) -> (usize, Option) { - self.iter.size_hint() - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> DoubleEndedIterator for Drain<'a, T> { - #[inline] - fn next_back(&mut self) -> Option { - self.iter.next_back().map(|elt| unsafe { ptr::read(elt) }) - } -} - -#[stable(feature = "drain", since = "1.6.0")] -impl<'a, T: 'a> ExactSizeIterator for Drain<'a, T> {} - -#[stable(feature = "fused", since = "1.26.0")] -impl<'a, T: 'a> FusedIterator for Drain<'a, T> {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for VecDeque { - fn eq(&self, other: &VecDeque) -> bool { - if self.len() != other.len() { - return false; - } - let (sa, sb) = self.as_slices(); - let (oa, ob) = other.as_slices(); - if sa.len() == oa.len() { - sa == oa && sb == ob - } else if sa.len() < oa.len() { - // Always divisible in three sections, for example: - // self: [a b c|d e f] - // other: [0 1 2 3|4 5] - // front = 3, mid = 1, - // [a b c] == [0 1 2] && [d] == [3] && [e f] == [4 5] - let front = sa.len(); - let mid = oa.len() - front; - - let (oa_front, oa_mid) = oa.split_at(front); - let (sb_mid, sb_back) = sb.split_at(mid); - debug_assert_eq!(sa.len(), oa_front.len()); - debug_assert_eq!(sb_mid.len(), oa_mid.len()); - debug_assert_eq!(sb_back.len(), ob.len()); - sa == oa_front && sb_mid == oa_mid && sb_back == ob - } else { - let front = oa.len(); - let mid = sa.len() - front; - - let (sa_front, sa_mid) = sa.split_at(front); - let (ob_mid, ob_back) = ob.split_at(mid); - debug_assert_eq!(sa_front.len(), oa.len()); - debug_assert_eq!(sa_mid.len(), ob_mid.len()); - debug_assert_eq!(sb.len(), ob_back.len()); - sa_front == oa && sa_mid == ob_mid && sb == ob_back - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for VecDeque {} - -macro_rules! __impl_slice_eq1 { - ($Lhs: ty, $Rhs: ty) => { - __impl_slice_eq1! { $Lhs, $Rhs, Sized } - }; - ($Lhs: ty, $Rhs: ty, $Bound: ident) => { - #[stable(feature = "vec-deque-partial-eq-slice", since = "1.17.0")] - impl<'a, 'b, A: $Bound, B> PartialEq<$Rhs> for $Lhs where A: PartialEq { - fn eq(&self, other: &$Rhs) -> bool { - if self.len() != other.len() { - return false; - } - let (sa, sb) = self.as_slices(); - let (oa, ob) = other[..].split_at(sa.len()); - sa == oa && sb == ob - } - } - } -} - -__impl_slice_eq1! { VecDeque, Vec } -__impl_slice_eq1! { VecDeque, &'b [B] } -__impl_slice_eq1! { VecDeque, &'b mut [B] } - -macro_rules! array_impls { - ($($N: expr)+) => { - $( - __impl_slice_eq1! { VecDeque, [B; $N] } - __impl_slice_eq1! { VecDeque, &'b [B; $N] } - __impl_slice_eq1! { VecDeque, &'b mut [B; $N] } - )+ - } -} - -array_impls! { - 0 1 2 3 4 5 6 7 8 9 - 10 11 12 13 14 15 16 17 18 19 - 20 21 22 23 24 25 26 27 28 29 - 30 31 32 -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for VecDeque { - fn partial_cmp(&self, other: &VecDeque) -> Option { - self.iter().partial_cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for VecDeque { - #[inline] - fn cmp(&self, other: &VecDeque) -> Ordering { - self.iter().cmp(other.iter()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for VecDeque { - fn hash(&self, state: &mut H) { - self.len().hash(state); - let (a, b) = self.as_slices(); - Hash::hash_slice(a, state); - Hash::hash_slice(b, state); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Index for VecDeque { - type Output = A; - - #[inline] - fn index(&self, index: usize) -> &A { - self.get(index).expect("Out of bounds access") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IndexMut for VecDeque { - #[inline] - fn index_mut(&mut self, index: usize) -> &mut A { - self.get_mut(index).expect("Out of bounds access") - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl FromIterator for VecDeque { - fn from_iter>(iter: T) -> VecDeque { - let iterator = iter.into_iter(); - let (lower, _) = iterator.size_hint(); - let mut deq = VecDeque::with_capacity(lower); - deq.extend(iterator); - deq - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl IntoIterator for VecDeque { - type Item = T; - type IntoIter = IntoIter; - - /// Consumes the `VecDeque` into a front-to-back iterator yielding elements by - /// value. - fn into_iter(self) -> IntoIter { - IntoIter { inner: self } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a VecDeque { - type Item = &'a T; - type IntoIter = Iter<'a, T>; - - fn into_iter(self) -> Iter<'a, T> { - self.iter() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl<'a, T> IntoIterator for &'a mut VecDeque { - type Item = &'a mut T; - type IntoIter = IterMut<'a, T>; - - fn into_iter(self) -> IterMut<'a, T> { - self.iter_mut() - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Extend for VecDeque { - fn extend>(&mut self, iter: T) { - for elt in iter { - self.push_back(elt); - } - } -} - -#[stable(feature = "extend_ref", since = "1.2.0")] -impl<'a, T: 'a + Copy> Extend<&'a T> for VecDeque { - fn extend>(&mut self, iter: I) { - self.extend(iter.into_iter().cloned()); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for VecDeque { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_list().entries(self).finish() - } -} - -#[stable(feature = "vecdeque_vec_conversions", since = "1.10.0")] -impl From> for VecDeque { - fn from(mut other: Vec) -> Self { - unsafe { - let other_buf = other.as_mut_ptr(); - let mut buf = RawVec::from_raw_parts(other_buf, other.capacity()); - let len = other.len(); - mem::forget(other); - - // We need to extend the buf if it's not a power of two, too small - // or doesn't have at least one free space - if !buf.cap().is_power_of_two() || (buf.cap() < (MINIMUM_CAPACITY + 1)) || - (buf.cap() == len) { - let cap = cmp::max(buf.cap() + 1, MINIMUM_CAPACITY + 1).next_power_of_two(); - buf.reserve_exact(len, cap - len); - } - - VecDeque { - tail: 0, - head: len, - buf, - } - } - } -} - -#[stable(feature = "vecdeque_vec_conversions", since = "1.10.0")] -impl From> for Vec { - fn from(other: VecDeque) -> Self { - unsafe { - let buf = other.buf.ptr(); - let len = other.len(); - let tail = other.tail; - let head = other.head; - let cap = other.cap(); - - // Need to move the ring to the front of the buffer, as vec will expect this. - if other.is_contiguous() { - ptr::copy(buf.offset(tail as isize), buf, len); - } else { - if (tail - head) >= cmp::min(cap - tail, head) { - // There is enough free space in the centre for the shortest block so we can - // do this in at most three copy moves. - if (cap - tail) > head { - // right hand block is the long one; move that enough for the left - ptr::copy(buf.offset(tail as isize), - buf.offset((tail - head) as isize), - cap - tail); - // copy left in the end - ptr::copy(buf, buf.offset((cap - head) as isize), head); - // shift the new thing to the start - ptr::copy(buf.offset((tail - head) as isize), buf, len); - } else { - // left hand block is the long one, we can do it in two! - ptr::copy(buf, buf.offset((cap - tail) as isize), head); - ptr::copy(buf.offset(tail as isize), buf, cap - tail); - } - } else { - // Need to use N swaps to move the ring - // We can use the space at the end of the ring as a temp store - - let mut left_edge: usize = 0; - let mut right_edge: usize = tail; - - // The general problem looks like this - // GHIJKLM...ABCDEF - before any swaps - // ABCDEFM...GHIJKL - after 1 pass of swaps - // ABCDEFGHIJM...KL - swap until the left edge reaches the temp store - // - then restart the algorithm with a new (smaller) store - // Sometimes the temp store is reached when the right edge is at the end - // of the buffer - this means we've hit the right order with fewer swaps! - // E.g - // EF..ABCD - // ABCDEF.. - after four only swaps we've finished - - while left_edge < len && right_edge != cap { - let mut right_offset = 0; - for i in left_edge..right_edge { - right_offset = (i - left_edge) % (cap - right_edge); - let src: isize = (right_edge + right_offset) as isize; - ptr::swap(buf.offset(i as isize), buf.offset(src)); - } - let n_ops = right_edge - left_edge; - left_edge += n_ops; - right_edge += right_offset + 1; - - } - } - - } - let out = Vec::from_raw_parts(buf, len, cap); - mem::forget(other); - out - } - } -} - -#[cfg(test)] -mod tests { - use test; - - use super::VecDeque; - - #[bench] - fn bench_push_back_100(b: &mut test::Bencher) { - let mut deq = VecDeque::with_capacity(101); - b.iter(|| { - for i in 0..100 { - deq.push_back(i); - } - deq.head = 0; - deq.tail = 0; - }) - } - - #[bench] - fn bench_push_front_100(b: &mut test::Bencher) { - let mut deq = VecDeque::with_capacity(101); - b.iter(|| { - for i in 0..100 { - deq.push_front(i); - } - deq.head = 0; - deq.tail = 0; - }) - } - - #[bench] - fn bench_pop_back_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); - - b.iter(|| { - deq.head = 100; - deq.tail = 0; - while !deq.is_empty() { - test::black_box(deq.pop_back()); - } - }) - } - - #[bench] - fn bench_pop_front_100(b: &mut test::Bencher) { - let mut deq = VecDeque::::with_capacity(101); - - b.iter(|| { - deq.head = 100; - deq.tail = 0; - while !deq.is_empty() { - test::black_box(deq.pop_front()); - } - }) - } - - #[test] - fn test_swap_front_back_remove() { - fn test(back: bool) { - // This test checks that every single combination of tail position and length is tested. - // Capacity 15 should be large enough to cover every case. - let mut tester = VecDeque::with_capacity(15); - let usable_cap = tester.capacity(); - let final_len = usable_cap / 2; - - for len in 0..final_len { - let expected: VecDeque<_> = if back { - (0..len).collect() - } else { - (0..len).rev().collect() - }; - for tail_pos in 0..usable_cap { - tester.tail = tail_pos; - tester.head = tail_pos; - if back { - for i in 0..len * 2 { - tester.push_front(i); - } - for i in 0..len { - assert_eq!(tester.swap_remove_back(i), Some(len * 2 - 1 - i)); - } - } else { - for i in 0..len * 2 { - tester.push_back(i); - } - for i in 0..len { - let idx = tester.len() - 1 - i; - assert_eq!(tester.swap_remove_front(idx), Some(len * 2 - 1 - i)); - } - } - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } - test(true); - test(false); - } - - #[test] - fn test_insert() { - // This test checks that every single combination of tail position, length, and - // insertion position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - - // len is the length *after* insertion - for len in 1..cap { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..cap { - for to_insert in 0..len { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - if i != to_insert { - tester.push_back(i); - } - } - tester.insert(to_insert, to_insert); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } - } - - #[test] - fn test_remove() { - // This test checks that every single combination of tail position, length, and - // removal position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - // len is the length *after* removal - for len in 0..cap - 1 { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..cap { - for to_remove in 0..len + 1 { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - if i == to_remove { - tester.push_back(1234); - } - tester.push_back(i); - } - if to_remove == len { - tester.push_back(1234); - } - tester.remove(to_remove); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } - } - - #[test] - fn test_drain() { - let mut tester: VecDeque = VecDeque::with_capacity(7); - - let cap = tester.capacity(); - for len in 0..cap + 1 { - for tail in 0..cap + 1 { - for drain_start in 0..len + 1 { - for drain_end in drain_start..len + 1 { - tester.tail = tail; - tester.head = tail; - for i in 0..len { - tester.push_back(i); - } - - // Check that we drain the correct values - let drained: VecDeque<_> = tester.drain(drain_start..drain_end).collect(); - let drained_expected: VecDeque<_> = (drain_start..drain_end).collect(); - assert_eq!(drained, drained_expected); - - // We shouldn't have changed the capacity or made the - // head or tail out of bounds - assert_eq!(tester.capacity(), cap); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - - // We should see the correct values in the VecDeque - let expected: VecDeque<_> = (0..drain_start) - .chain(drain_end..len) - .collect(); - assert_eq!(expected, tester); - } - } - } - } - } - - #[test] - fn test_shrink_to_fit() { - // This test checks that every single combination of head and tail position, - // is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - tester.reserve(63); - let max_cap = tester.capacity(); - - for len in 0..cap + 1 { - // 0, 1, 2, .., len - 1 - let expected = (0..).take(len).collect::>(); - for tail_pos in 0..max_cap + 1 { - tester.tail = tail_pos; - tester.head = tail_pos; - tester.reserve(63); - for i in 0..len { - tester.push_back(i); - } - tester.shrink_to_fit(); - assert!(tester.capacity() <= cap); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert_eq!(tester, expected); - } - } - } - - #[test] - fn test_split_off() { - // This test checks that every single combination of tail position, length, and - // split position is tested. Capacity 15 should be large enough to cover every case. - - let mut tester = VecDeque::with_capacity(15); - // can't guarantee we got 15, so have to get what we got. - // 15 would be great, but we will definitely get 2^k - 1, for k >= 4, or else - // this test isn't covering what it wants to - let cap = tester.capacity(); - - // len is the length *before* splitting - for len in 0..cap { - // index to split at - for at in 0..len + 1 { - // 0, 1, 2, .., at - 1 (may be empty) - let expected_self = (0..).take(at).collect::>(); - // at, at + 1, .., len - 1 (may be empty) - let expected_other = (at..).take(len - at).collect::>(); - - for tail_pos in 0..cap { - tester.tail = tail_pos; - tester.head = tail_pos; - for i in 0..len { - tester.push_back(i); - } - let result = tester.split_off(at); - assert!(tester.tail < tester.cap()); - assert!(tester.head < tester.cap()); - assert!(result.tail < result.cap()); - assert!(result.head < result.cap()); - assert_eq!(tester, expected_self); - assert_eq!(result, expected_other); - } - } - } - } - - #[test] - fn test_from_vec() { - use super::super::vec::Vec; - for cap in 0..35 { - for len in 0..cap + 1 { - let mut vec = Vec::with_capacity(cap); - vec.extend(0..len); - - let vd = VecDeque::from(vec.clone()); - assert!(vd.cap().is_power_of_two()); - assert_eq!(vd.len(), vec.len()); - assert!(vd.into_iter().eq(vec)); - } - } - } - - #[test] - fn test_vec_from_vecdeque() { - use super::super::vec::Vec; - - fn create_vec_and_test_convert(cap: usize, offset: usize, len: usize) { - let mut vd = VecDeque::with_capacity(cap); - for _ in 0..offset { - vd.push_back(0); - vd.pop_front(); - } - vd.extend(0..len); - - let vec: Vec<_> = Vec::from(vd.clone()); - assert_eq!(vec.len(), vd.len()); - assert!(vec.into_iter().eq(vd)); - } - - for cap_pwr in 0..7 { - // Make capacity as a (2^x)-1, so that the ring size is 2^x - let cap = (2i32.pow(cap_pwr) - 1) as usize; - - // In these cases there is enough free space to solve it with copies - for len in 0..((cap + 1) / 2) { - // Test contiguous cases - for offset in 0..(cap - len) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at end of buffer is bigger than block at start - for offset in (cap - len)..(cap - (len / 2)) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at start of buffer is bigger than block at end - for offset in (cap - (len / 2))..cap { - create_vec_and_test_convert(cap, offset, len) - } - } - - // Now there's not (necessarily) space to straighten the ring with simple copies, - // the ring will use swapping when: - // (cap + 1 - offset) > (cap + 1 - len) && (len - (cap + 1 - offset)) > (cap + 1 - len)) - // right block size > free space && left block size > free space - for len in ((cap + 1) / 2)..cap { - // Test contiguous cases - for offset in 0..(cap - len) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at end of buffer is bigger than block at start - for offset in (cap - len)..(cap - (len / 2)) { - create_vec_and_test_convert(cap, offset, len) - } - - // Test cases where block at start of buffer is bigger than block at end - for offset in (cap - (len / 2))..cap { - create_vec_and_test_convert(cap, offset, len) - } - } - } - } - -} diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 42113414183..643426c377b 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -424,13 +424,13 @@ #[doc(hidden)] pub use ops::Bound; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::{BinaryHeap, BTreeMap, BTreeSet}; +pub use alloc_crate::collections::{BinaryHeap, BTreeMap, BTreeSet}; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::{LinkedList, VecDeque}; +pub use alloc_crate::collections::{LinkedList, VecDeque}; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::{binary_heap, btree_map, btree_set}; +pub use alloc_crate::collections::{binary_heap, btree_map, btree_set}; #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::{linked_list, vec_deque}; +pub use alloc_crate::collections::{linked_list, vec_deque}; #[stable(feature = "rust1", since = "1.0.0")] pub use self::hash_map::HashMap; -- cgit 1.4.1-3-g733a5 From b0547cea0ae50f49619ded26f43d0d55a1674b14 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 03:56:35 +0200 Subject: Move core::alloc::CollectionAllocErr to alloc::collections --- src/liballoc/collections/mod.rs | 29 +++++++++++++++++++++++++++++ src/liballoc/collections/vec_deque.rs | 2 +- src/liballoc/raw_vec.rs | 4 ++-- src/liballoc/string.rs | 2 +- src/liballoc/vec.rs | 2 +- src/libcore/alloc.rs | 28 ---------------------------- src/libstd/collections/hash/map.rs | 2 +- src/libstd/collections/hash/table.rs | 3 ++- src/libstd/collections/mod.rs | 2 +- 9 files changed, 38 insertions(+), 36 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/collections/mod.rs b/src/liballoc/collections/mod.rs index 35c816a1ceb..96e0eb633b2 100644 --- a/src/liballoc/collections/mod.rs +++ b/src/liballoc/collections/mod.rs @@ -51,6 +51,35 @@ pub use self::linked_list::LinkedList; #[doc(no_inline)] pub use self::vec_deque::VecDeque; +use alloc::{AllocErr, LayoutErr}; + +/// Augments `AllocErr` with a CapacityOverflow variant. +#[derive(Clone, PartialEq, Eq, Debug)] +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +pub enum CollectionAllocErr { + /// Error due to the computed capacity exceeding the collection's maximum + /// (usually `isize::MAX` bytes). + CapacityOverflow, + /// Error due to the allocator (see the `AllocErr` type's docs). + AllocErr, +} + +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From for CollectionAllocErr { + #[inline] + fn from(AllocErr: AllocErr) -> Self { + CollectionAllocErr::AllocErr + } +} + +#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] +impl From for CollectionAllocErr { + #[inline] + fn from(_: LayoutErr) -> Self { + CollectionAllocErr::CapacityOverflow + } +} + /// An intermediate trait for specialization of `Extend`. #[doc(hidden)] trait SpecExtend { diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index 4753d36415c..ba92b886138 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -30,7 +30,7 @@ use core::slice; use core::hash::{Hash, Hasher}; use core::cmp; -use alloc::CollectionAllocErr; +use collections::CollectionAllocErr; use raw_vec::RawVec; use vec::Vec; diff --git a/src/liballoc/raw_vec.rs b/src/liballoc/raw_vec.rs index 5095bbe96cc..4f2686abf45 100644 --- a/src/liballoc/raw_vec.rs +++ b/src/liballoc/raw_vec.rs @@ -18,8 +18,8 @@ use core::ptr::{self, NonNull, Unique}; use core::slice; use alloc::{Alloc, Layout, Global, handle_alloc_error}; -use alloc::CollectionAllocErr; -use alloc::CollectionAllocErr::*; +use collections::CollectionAllocErr; +use collections::CollectionAllocErr::*; use boxed::Box; /// A low-level utility for more ergonomically allocating, reallocating, and deallocating diff --git a/src/liballoc/string.rs b/src/liballoc/string.rs index a988b6a26d9..6b28687a060 100644 --- a/src/liballoc/string.rs +++ b/src/liballoc/string.rs @@ -66,7 +66,7 @@ use core::ptr; use core::str::pattern::Pattern; use core::str::lossy; -use alloc::CollectionAllocErr; +use collections::CollectionAllocErr; use borrow::{Cow, ToOwned}; use boxed::Box; use str::{self, from_boxed_utf8_unchecked, FromStr, Utf8Error, Chars}; diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 752a6c966d5..fbbaced540e 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -80,7 +80,7 @@ use core::ptr; use core::ptr::NonNull; use core::slice; -use alloc::CollectionAllocErr; +use collections::CollectionAllocErr; use borrow::ToOwned; use borrow::Cow; use boxed::Box; diff --git a/src/libcore/alloc.rs b/src/libcore/alloc.rs index 91447e01ad4..01221aecb62 100644 --- a/src/libcore/alloc.rs +++ b/src/libcore/alloc.rs @@ -385,34 +385,6 @@ impl fmt::Display for CannotReallocInPlace { } } -/// Augments `AllocErr` with a CapacityOverflow variant. -// FIXME: should this be in libcore or liballoc? -#[derive(Clone, PartialEq, Eq, Debug)] -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -pub enum CollectionAllocErr { - /// Error due to the computed capacity exceeding the collection's maximum - /// (usually `isize::MAX` bytes). - CapacityOverflow, - /// Error due to the allocator (see the `AllocErr` type's docs). - AllocErr, -} - -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -impl From for CollectionAllocErr { - #[inline] - fn from(AllocErr: AllocErr) -> Self { - CollectionAllocErr::AllocErr - } -} - -#[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -impl From for CollectionAllocErr { - #[inline] - fn from(_: LayoutErr) -> Self { - CollectionAllocErr::CapacityOverflow - } -} - /// A memory allocator that can be registered as the standard library’s default /// though the `#[global_allocator]` attributes. /// diff --git a/src/libstd/collections/hash/map.rs b/src/libstd/collections/hash/map.rs index ee8c1dc81ad..91912e5f241 100644 --- a/src/libstd/collections/hash/map.rs +++ b/src/libstd/collections/hash/map.rs @@ -11,7 +11,7 @@ use self::Entry::*; use self::VacantEntryState::*; -use alloc::CollectionAllocErr; +use collections::CollectionAllocErr; use cell::Cell; use borrow::Borrow; use cmp::max; diff --git a/src/libstd/collections/hash/table.rs b/src/libstd/collections/hash/table.rs index d14b754ddb6..2b319186a8d 100644 --- a/src/libstd/collections/hash/table.rs +++ b/src/libstd/collections/hash/table.rs @@ -8,7 +8,8 @@ // option. This file may not be copied, modified, or distributed // except according to those terms. -use alloc::{Global, Alloc, Layout, LayoutErr, CollectionAllocErr, handle_alloc_error}; +use alloc::{Global, Alloc, Layout, LayoutErr, handle_alloc_error}; +use collections::CollectionAllocErr; use hash::{BuildHasher, Hash, Hasher}; use marker; use mem::{size_of, needs_drop}; diff --git a/src/libstd/collections/mod.rs b/src/libstd/collections/mod.rs index 643426c377b..8d2c82bc0aa 100644 --- a/src/libstd/collections/mod.rs +++ b/src/libstd/collections/mod.rs @@ -438,7 +438,7 @@ pub use self::hash_map::HashMap; pub use self::hash_set::HashSet; #[unstable(feature = "try_reserve", reason = "new API", issue="48043")] -pub use alloc::CollectionAllocErr; +pub use alloc_crate::collections::CollectionAllocErr; mod hash; -- cgit 1.4.1-3-g733a5 From 3394fb7bb7f08c045f9a82bb92272418c855859d Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 03:59:59 +0200 Subject: Remove the Vec and String reexports at the root of the alloc crate MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit … since `std` has no corresponding reexports. Use `alloc::vec::Vec` and `alloc::string::String` instead. --- src/liballoc/lib.rs | 5 ----- 1 file changed, 5 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index e8be9ecfa36..9e1740473fe 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -179,8 +179,3 @@ pub mod vec; mod std { pub use core::ops; // RangeFull } - -#[doc(no_inline)] -pub use string::String; -#[doc(no_inline)] -pub use vec::Vec; -- cgit 1.4.1-3-g733a5 From c7638edf5293dd471d951e64671d60febd0b628c Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 15 Jun 2018 04:07:09 +0200 Subject: Rename alloc::arc to alloc::sync, to match std::sync --- src/liballoc/arc.rs | 1936 ------------------------------------------------ src/liballoc/lib.rs | 4 +- src/liballoc/sync.rs | 1936 ++++++++++++++++++++++++++++++++++++++++++++++++ src/liballoc/task.rs | 2 +- src/libstd/sync/mod.rs | 2 +- 5 files changed, 1940 insertions(+), 1940 deletions(-) delete mode 100644 src/liballoc/arc.rs create mode 100644 src/liballoc/sync.rs (limited to 'src/liballoc') diff --git a/src/liballoc/arc.rs b/src/liballoc/arc.rs deleted file mode 100644 index 2abd9c85c57..00000000000 --- a/src/liballoc/arc.rs +++ /dev/null @@ -1,1936 +0,0 @@ -// Copyright 2012-2014 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![stable(feature = "rust1", since = "1.0.0")] - -//! Thread-safe reference-counting pointers. -//! -//! See the [`Arc`][arc] documentation for more details. -//! -//! [arc]: struct.Arc.html - -use core::any::Any; -use core::sync::atomic; -use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; -use core::borrow; -use core::fmt; -use core::cmp::Ordering; -use core::intrinsics::abort; -use core::mem::{self, align_of_val, size_of_val}; -use core::ops::Deref; -use core::ops::CoerceUnsized; -use core::ptr::{self, NonNull}; -use core::marker::{Unsize, PhantomData}; -use core::hash::{Hash, Hasher}; -use core::{isize, usize}; -use core::convert::From; - -use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; -use boxed::Box; -use string::String; -use vec::Vec; - -/// A soft limit on the amount of references that may be made to an `Arc`. -/// -/// Going above this limit will abort your program (although not -/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. -const MAX_REFCOUNT: usize = (isize::MAX) as usize; - -/// A sentinel value that is used for the pointer of `Weak::new()`. -const WEAK_EMPTY: usize = 1; - -/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically -/// Reference Counted'. -/// -/// The type `Arc` provides shared ownership of a value of type `T`, -/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces -/// a new pointer to the same value in the heap. When the last `Arc` -/// pointer to a given value is destroyed, the pointed-to value is -/// also destroyed. -/// -/// Shared references in Rust disallow mutation by default, and `Arc` is no -/// exception: you cannot generally obtain a mutable reference to something -/// inside an `Arc`. If you need to mutate through an `Arc`, use -/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] -/// types. -/// -/// ## Thread Safety -/// -/// Unlike [`Rc`], `Arc` uses atomic operations for its reference -/// counting. This means that it is thread-safe. The disadvantage is that -/// atomic operations are more expensive than ordinary memory accesses. If you -/// are not sharing reference-counted values between threads, consider using -/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the -/// compiler will catch any attempt to send an [`Rc`] between threads. -/// However, a library might choose `Arc` in order to give library consumers -/// more flexibility. -/// -/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements -/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an -/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at -/// first: after all, isn't the point of `Arc` thread safety? The key is -/// this: `Arc` makes it thread safe to have multiple ownership of the same -/// data, but it doesn't add thread safety to its data. Consider -/// `Arc<`[`RefCell`]`>`. [`RefCell`] isn't [`Sync`], and if `Arc` was always -/// [`Send`], `Arc<`[`RefCell`]`>` would be as well. But then we'd have a problem: -/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using -/// non-atomic operations. -/// -/// In the end, this means that you may need to pair `Arc` with some sort of -/// [`std::sync`] type, usually [`Mutex`][mutex]. -/// -/// ## Breaking cycles with `Weak` -/// -/// The [`downgrade`][downgrade] method can be used to create a non-owning -/// [`Weak`][weak] pointer. A [`Weak`][weak] pointer can be [`upgrade`][upgrade]d -/// to an `Arc`, but this will return [`None`] if the value has already been -/// dropped. -/// -/// A cycle between `Arc` pointers will never be deallocated. For this reason, -/// [`Weak`][weak] is used to break cycles. For example, a tree could have -/// strong `Arc` pointers from parent nodes to children, and [`Weak`][weak] -/// pointers from children back to their parents. -/// -/// # Cloning references -/// -/// Creating a new reference from an existing reference counted pointer is done using the -/// `Clone` trait implemented for [`Arc`][arc] and [`Weak`][weak]. -/// -/// ``` -/// use std::sync::Arc; -/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); -/// // The two syntaxes below are equivalent. -/// let a = foo.clone(); -/// let b = Arc::clone(&foo); -/// // a and b both point to the same memory location as foo. -/// ``` -/// -/// The [`Arc::clone(&from)`] syntax is the most idiomatic because it conveys more explicitly -/// the meaning of the code. In the example above, this syntax makes it easier to see that -/// this code is creating a new reference rather than copying the whole content of foo. -/// -/// ## `Deref` behavior -/// -/// `Arc` automatically dereferences to `T` (via the [`Deref`][deref] trait), -/// so you can call `T`'s methods on a value of type `Arc`. To avoid name -/// clashes with `T`'s methods, the methods of `Arc` itself are [associated -/// functions][assoc], called using function-like syntax: -/// -/// ``` -/// use std::sync::Arc; -/// let my_arc = Arc::new(()); -/// -/// Arc::downgrade(&my_arc); -/// ``` -/// -/// [`Weak`][weak] does not auto-dereference to `T`, because the value may have -/// already been destroyed. -/// -/// [arc]: struct.Arc.html -/// [weak]: struct.Weak.html -/// [`Rc`]: ../../std/rc/struct.Rc.html -/// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone -/// [mutex]: ../../std/sync/struct.Mutex.html -/// [rwlock]: ../../std/sync/struct.RwLock.html -/// [atomic]: ../../std/sync/atomic/index.html -/// [`Send`]: ../../std/marker/trait.Send.html -/// [`Sync`]: ../../std/marker/trait.Sync.html -/// [deref]: ../../std/ops/trait.Deref.html -/// [downgrade]: struct.Arc.html#method.downgrade -/// [upgrade]: struct.Weak.html#method.upgrade -/// [`None`]: ../../std/option/enum.Option.html#variant.None -/// [assoc]: ../../book/first-edition/method-syntax.html#associated-functions -/// [`RefCell`]: ../../std/cell/struct.RefCell.html -/// [`std::sync`]: ../../std/sync/index.html -/// [`Arc::clone(&from)`]: #method.clone -/// -/// # Examples -/// -/// Sharing some immutable data between threads: -/// -// Note that we **do not** run these tests here. The windows builders get super -// unhappy if a thread outlives the main thread and then exits at the same time -// (something deadlocks) so we just avoid this entirely by not running these -// tests. -/// ```no_run -/// use std::sync::Arc; -/// use std::thread; -/// -/// let five = Arc::new(5); -/// -/// for _ in 0..10 { -/// let five = Arc::clone(&five); -/// -/// thread::spawn(move || { -/// println!("{:?}", five); -/// }); -/// } -/// ``` -/// -/// Sharing a mutable [`AtomicUsize`]: -/// -/// [`AtomicUsize`]: ../../std/sync/atomic/struct.AtomicUsize.html -/// -/// ```no_run -/// use std::sync::Arc; -/// use std::sync::atomic::{AtomicUsize, Ordering}; -/// use std::thread; -/// -/// let val = Arc::new(AtomicUsize::new(5)); -/// -/// for _ in 0..10 { -/// let val = Arc::clone(&val); -/// -/// thread::spawn(move || { -/// let v = val.fetch_add(1, Ordering::SeqCst); -/// println!("{:?}", v); -/// }); -/// } -/// ``` -/// -/// See the [`rc` documentation][rc_examples] for more examples of reference -/// counting in general. -/// -/// [rc_examples]: ../../std/rc/index.html#examples -#[stable(feature = "rust1", since = "1.0.0")] -pub struct Arc { - ptr: NonNull>, - phantom: PhantomData, -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Send for Arc {} -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl Sync for Arc {} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Arc {} - -/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the -/// managed value. The value is accessed by calling [`upgrade`] on the `Weak` -/// pointer, which returns an [`Option`]`<`[`Arc`]`>`. -/// -/// Since a `Weak` reference does not count towards ownership, it will not -/// prevent the inner value from being dropped, and `Weak` itself makes no -/// guarantees about the value still being present and may return [`None`] -/// when [`upgrade`]d. -/// -/// A `Weak` pointer is useful for keeping a temporary reference to the value -/// within [`Arc`] without extending its lifetime. It is also used to prevent -/// circular references between [`Arc`] pointers, since mutual owning references -/// would never allow either [`Arc`] to be dropped. For example, a tree could -/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` -/// pointers from children back to their parents. -/// -/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. -/// -/// [`Arc`]: struct.Arc.html -/// [`Arc::downgrade`]: struct.Arc.html#method.downgrade -/// [`upgrade`]: struct.Weak.html#method.upgrade -/// [`Option`]: ../../std/option/enum.Option.html -/// [`None`]: ../../std/option/enum.Option.html#variant.None -#[stable(feature = "arc_weak", since = "1.4.0")] -pub struct Weak { - // This is a `NonNull` to allow optimizing the size of this type in enums, - // but it is actually not truly "non-null". A `Weak::new()` will set this - // to a sentinel value, instead of needing to allocate some space in the - // heap. - ptr: NonNull>, -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Send for Weak {} -#[stable(feature = "arc_weak", since = "1.4.0")] -unsafe impl Sync for Weak {} - -#[unstable(feature = "coerce_unsized", issue = "27732")] -impl, U: ?Sized> CoerceUnsized> for Weak {} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl fmt::Debug for Weak { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - write!(f, "(Weak)") - } -} - -struct ArcInner { - strong: atomic::AtomicUsize, - - // the value usize::MAX acts as a sentinel for temporarily "locking" the - // ability to upgrade weak pointers or downgrade strong ones; this is used - // to avoid races in `make_mut` and `get_mut`. - weak: atomic::AtomicUsize, - - data: T, -} - -unsafe impl Send for ArcInner {} -unsafe impl Sync for ArcInner {} - -impl Arc { - /// Constructs a new `Arc`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// ``` - #[inline] - #[stable(feature = "rust1", since = "1.0.0")] - pub fn new(data: T) -> Arc { - // Start the weak pointer count as 1 which is the weak pointer that's - // held by all the strong pointers (kinda), see std/rc.rs for more info - let x: Box<_> = box ArcInner { - strong: atomic::AtomicUsize::new(1), - weak: atomic::AtomicUsize::new(1), - data, - }; - Arc { ptr: Box::into_raw_non_null(x), phantom: PhantomData } - } - - /// Returns the contained value, if the `Arc` has exactly one strong reference. - /// - /// Otherwise, an [`Err`][result] is returned with the same `Arc` that was - /// passed in. - /// - /// This will succeed even if there are outstanding weak references. - /// - /// [result]: ../../std/result/enum.Result.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(3); - /// assert_eq!(Arc::try_unwrap(x), Ok(3)); - /// - /// let x = Arc::new(4); - /// let _y = Arc::clone(&x); - /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn try_unwrap(this: Self) -> Result { - // See `drop` for why all these atomics are like this - if this.inner().strong.compare_exchange(1, 0, Release, Relaxed).is_err() { - return Err(this); - } - - atomic::fence(Acquire); - - unsafe { - let elem = ptr::read(&this.ptr.as_ref().data); - - // Make a weak pointer to clean up the implicit strong-weak reference - let _weak = Weak { ptr: this.ptr }; - mem::forget(this); - - Ok(elem) - } - } -} - -impl Arc { - /// Consumes the `Arc`, returning the wrapped pointer. - /// - /// To avoid a memory leak the pointer must be converted back to an `Arc` using - /// [`Arc::from_raw`][from_raw]. - /// - /// [from_raw]: struct.Arc.html#method.from_raw - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(10); - /// let x_ptr = Arc::into_raw(x); - /// assert_eq!(unsafe { *x_ptr }, 10); - /// ``` - #[stable(feature = "rc_raw", since = "1.17.0")] - pub fn into_raw(this: Self) -> *const T { - let ptr: *const T = &*this; - mem::forget(this); - ptr - } - - /// Constructs an `Arc` from a raw pointer. - /// - /// The raw pointer must have been previously returned by a call to a - /// [`Arc::into_raw`][into_raw]. - /// - /// This function is unsafe because improper use may lead to memory problems. For example, a - /// double-free may occur if the function is called twice on the same raw pointer. - /// - /// [into_raw]: struct.Arc.html#method.into_raw - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x = Arc::new(10); - /// let x_ptr = Arc::into_raw(x); - /// - /// unsafe { - /// // Convert back to an `Arc` to prevent leak. - /// let x = Arc::from_raw(x_ptr); - /// assert_eq!(*x, 10); - /// - /// // Further calls to `Arc::from_raw(x_ptr)` would be memory unsafe. - /// } - /// - /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! - /// ``` - #[stable(feature = "rc_raw", since = "1.17.0")] - pub unsafe fn from_raw(ptr: *const T) -> Self { - // Align the unsized value to the end of the ArcInner. - // Because it is ?Sized, it will always be the last field in memory. - let align = align_of_val(&*ptr); - let layout = Layout::new::>(); - let offset = (layout.size() + layout.padding_needed_for(align)) as isize; - - // Reverse the offset to find the original ArcInner. - let fake_ptr = ptr as *mut ArcInner; - let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); - - Arc { - ptr: NonNull::new_unchecked(arc_ptr), - phantom: PhantomData, - } - } - - /// Creates a new [`Weak`][weak] pointer to this value. - /// - /// [weak]: struct.Weak.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// ``` - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn downgrade(this: &Self) -> Weak { - // This Relaxed is OK because we're checking the value in the CAS - // below. - let mut cur = this.inner().weak.load(Relaxed); - - loop { - // check if the weak counter is currently "locked"; if so, spin. - if cur == usize::MAX { - cur = this.inner().weak.load(Relaxed); - continue; - } - - // NOTE: this code currently ignores the possibility of overflow - // into usize::MAX; in general both Rc and Arc need to be adjusted - // to deal with overflow. - - // Unlike with Clone(), we need this to be an Acquire read to - // synchronize with the write coming from `is_unique`, so that the - // events prior to that write happen before this read. - match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - Ok(_) => return Weak { ptr: this.ptr }, - Err(old) => cur = old, - } - } - } - - /// Gets the number of [`Weak`][weak] pointers to this value. - /// - /// [weak]: struct.Weak.html - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the weak count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _weak_five = Arc::downgrade(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` or `Weak` between threads. - /// assert_eq!(1, Arc::weak_count(&five)); - /// ``` - #[inline] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn weak_count(this: &Self) -> usize { - let cnt = this.inner().weak.load(SeqCst); - // If the weak count is currently locked, the value of the - // count was 0 just before taking the lock. - if cnt == usize::MAX { 0 } else { cnt - 1 } - } - - /// Gets the number of strong (`Arc`) pointers to this value. - /// - /// # Safety - /// - /// This method by itself is safe, but using it correctly requires extra care. - /// Another thread can change the strong count at any time, - /// including potentially between calling this method and acting on the result. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let _also_five = Arc::clone(&five); - /// - /// // This assertion is deterministic because we haven't shared - /// // the `Arc` between threads. - /// assert_eq!(2, Arc::strong_count(&five)); - /// ``` - #[inline] - #[stable(feature = "arc_counts", since = "1.15.0")] - pub fn strong_count(this: &Self) -> usize { - this.inner().strong.load(SeqCst) - } - - #[inline] - fn inner(&self) -> &ArcInner { - // This unsafety is ok because while this arc is alive we're guaranteed - // that the inner pointer is valid. Furthermore, we know that the - // `ArcInner` structure itself is `Sync` because the inner data is - // `Sync` as well, so we're ok loaning out an immutable pointer to these - // contents. - unsafe { self.ptr.as_ref() } - } - - // Non-inlined part of `drop`. - #[inline(never)] - unsafe fn drop_slow(&mut self) { - // Destroy the data at this time, even though we may not free the box - // allocation itself (there may still be weak pointers lying around). - ptr::drop_in_place(&mut self.ptr.as_mut().data); - - if self.inner().weak.fetch_sub(1, Release) == 1 { - atomic::fence(Acquire); - Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) - } - } - - #[inline] - #[stable(feature = "ptr_eq", since = "1.17.0")] - /// Returns true if the two `Arc`s point to the same value (not - /// just values that compare as equal). - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// let same_five = Arc::clone(&five); - /// let other_five = Arc::new(5); - /// - /// assert!(Arc::ptr_eq(&five, &same_five)); - /// assert!(!Arc::ptr_eq(&five, &other_five)); - /// ``` - pub fn ptr_eq(this: &Self, other: &Self) -> bool { - this.ptr.as_ptr() == other.ptr.as_ptr() - } -} - -impl Arc { - // Allocates an `ArcInner` with sufficient space for an unsized value - unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { - // Create a fake ArcInner to find allocation size and alignment - let fake_ptr = ptr as *mut ArcInner; - - let layout = Layout::for_value(&*fake_ptr); - - let mem = Global.alloc(layout) - .unwrap_or_else(|_| handle_alloc_error(layout)); - - // Initialize the real ArcInner - let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; - - ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); - ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); - - inner - } - - fn from_box(v: Box) -> Arc { - unsafe { - let box_unique = Box::into_unique(v); - let bptr = box_unique.as_ptr(); - - let value_size = size_of_val(&*bptr); - let ptr = Self::allocate_for_ptr(bptr); - - // Copy value as bytes - ptr::copy_nonoverlapping( - bptr as *const T as *const u8, - &mut (*ptr).data as *mut _ as *mut u8, - value_size); - - // Free the allocation without dropping its contents - box_free(box_unique); - - Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } - } - } -} - -// Sets the data pointer of a `?Sized` raw pointer. -// -// For a slice/trait object, this sets the `data` field and leaves the rest -// unchanged. For a sized raw pointer, this simply sets the pointer. -unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { - ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); - ptr -} - -impl Arc<[T]> { - // Copy elements from slice into newly allocated Arc<[T]> - // - // Unsafe because the caller must either take ownership or bind `T: Copy` - unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { - let v_ptr = v as *const [T]; - let ptr = Self::allocate_for_ptr(v_ptr); - - ptr::copy_nonoverlapping( - v.as_ptr(), - &mut (*ptr).data as *mut [T] as *mut T, - v.len()); - - Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } - } -} - -// Specialization trait used for From<&[T]> -trait ArcFromSlice { - fn from_slice(slice: &[T]) -> Self; -} - -impl ArcFromSlice for Arc<[T]> { - #[inline] - default fn from_slice(v: &[T]) -> Self { - // Panic guard while cloning T elements. - // In the event of a panic, elements that have been written - // into the new ArcInner will be dropped, then the memory freed. - struct Guard { - mem: NonNull, - elems: *mut T, - layout: Layout, - n_elems: usize, - } - - impl Drop for Guard { - fn drop(&mut self) { - use core::slice::from_raw_parts_mut; - - unsafe { - let slice = from_raw_parts_mut(self.elems, self.n_elems); - ptr::drop_in_place(slice); - - Global.dealloc(self.mem.cast(), self.layout.clone()); - } - } - } - - unsafe { - let v_ptr = v as *const [T]; - let ptr = Self::allocate_for_ptr(v_ptr); - - let mem = ptr as *mut _ as *mut u8; - let layout = Layout::for_value(&*ptr); - - // Pointer to first element - let elems = &mut (*ptr).data as *mut [T] as *mut T; - - let mut guard = Guard{ - mem: NonNull::new_unchecked(mem), - elems: elems, - layout: layout, - n_elems: 0, - }; - - for (i, item) in v.iter().enumerate() { - ptr::write(elems.offset(i as isize), item.clone()); - guard.n_elems += 1; - } - - // All clear. Forget the guard so it doesn't free the new ArcInner. - mem::forget(guard); - - Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } - } - } -} - -impl ArcFromSlice for Arc<[T]> { - #[inline] - fn from_slice(v: &[T]) -> Self { - unsafe { Arc::copy_from_slice(v) } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Clone for Arc { - /// Makes a clone of the `Arc` pointer. - /// - /// This creates another pointer to the same inner value, increasing the - /// strong reference count. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// Arc::clone(&five); - /// ``` - #[inline] - fn clone(&self) -> Arc { - // Using a relaxed ordering is alright here, as knowledge of the - // original reference prevents other threads from erroneously deleting - // the object. - // - // As explained in the [Boost documentation][1], Increasing the - // reference counter can always be done with memory_order_relaxed: New - // references to an object can only be formed from an existing - // reference, and passing an existing reference from one thread to - // another must already provide any required synchronization. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - let old_size = self.inner().strong.fetch_add(1, Relaxed); - - // However we need to guard against massive refcounts in case someone - // is `mem::forget`ing Arcs. If we don't do this the count can overflow - // and users will use-after free. We racily saturate to `isize::MAX` on - // the assumption that there aren't ~2 billion threads incrementing - // the reference count at once. This branch will never be taken in - // any realistic program. - // - // We abort because such a program is incredibly degenerate, and we - // don't care to support it. - if old_size > MAX_REFCOUNT { - unsafe { - abort(); - } - } - - Arc { ptr: self.ptr, phantom: PhantomData } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Deref for Arc { - type Target = T; - - #[inline] - fn deref(&self) -> &T { - &self.inner().data - } -} - -impl Arc { - /// Makes a mutable reference into the given `Arc`. - /// - /// If there are other `Arc` or [`Weak`][weak] pointers to the same value, - /// then `make_mut` will invoke [`clone`][clone] on the inner value to - /// ensure unique ownership. This is also referred to as clone-on-write. - /// - /// See also [`get_mut`][get_mut], which will fail rather than cloning. - /// - /// [weak]: struct.Weak.html - /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone - /// [get_mut]: struct.Arc.html#method.get_mut - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut data = Arc::new(5); - /// - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// let mut other_data = Arc::clone(&data); // Won't clone inner data - /// *Arc::make_mut(&mut data) += 1; // Clones inner data - /// *Arc::make_mut(&mut data) += 1; // Won't clone anything - /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything - /// - /// // Now `data` and `other_data` point to different values. - /// assert_eq!(*data, 8); - /// assert_eq!(*other_data, 12); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn make_mut(this: &mut Self) -> &mut T { - // Note that we hold both a strong reference and a weak reference. - // Thus, releasing our strong reference only will not, by itself, cause - // the memory to be deallocated. - // - // Use Acquire to ensure that we see any writes to `weak` that happen - // before release writes (i.e., decrements) to `strong`. Since we hold a - // weak count, there's no chance the ArcInner itself could be - // deallocated. - if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { - // Another strong pointer exists; clone - *this = Arc::new((**this).clone()); - } else if this.inner().weak.load(Relaxed) != 1 { - // Relaxed suffices in the above because this is fundamentally an - // optimization: we are always racing with weak pointers being - // dropped. Worst case, we end up allocated a new Arc unnecessarily. - - // We removed the last strong ref, but there are additional weak - // refs remaining. We'll move the contents to a new Arc, and - // invalidate the other weak refs. - - // Note that it is not possible for the read of `weak` to yield - // usize::MAX (i.e., locked), since the weak count can only be - // locked by a thread with a strong reference. - - // Materialize our own implicit weak pointer, so that it can clean - // up the ArcInner as needed. - let weak = Weak { ptr: this.ptr }; - - // mark the data itself as already deallocated - unsafe { - // there is no data race in the implicit write caused by `read` - // here (due to zeroing) because data is no longer accessed by - // other threads (due to there being no more strong refs at this - // point). - let mut swap = Arc::new(ptr::read(&weak.ptr.as_ref().data)); - mem::swap(this, &mut swap); - mem::forget(swap); - } - } else { - // We were the sole reference of either kind; bump back up the - // strong ref count. - this.inner().strong.store(1, Release); - } - - // As with `get_mut()`, the unsafety is ok because our reference was - // either unique to begin with, or became one upon cloning the contents. - unsafe { - &mut this.ptr.as_mut().data - } - } -} - -impl Arc { - /// Returns a mutable reference to the inner value, if there are - /// no other `Arc` or [`Weak`][weak] pointers to the same value. - /// - /// Returns [`None`][option] otherwise, because it is not safe to - /// mutate a shared value. - /// - /// See also [`make_mut`][make_mut], which will [`clone`][clone] - /// the inner value when it's shared. - /// - /// [weak]: struct.Weak.html - /// [option]: ../../std/option/enum.Option.html - /// [make_mut]: struct.Arc.html#method.make_mut - /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let mut x = Arc::new(3); - /// *Arc::get_mut(&mut x).unwrap() = 4; - /// assert_eq!(*x, 4); - /// - /// let _y = Arc::clone(&x); - /// assert!(Arc::get_mut(&mut x).is_none()); - /// ``` - #[inline] - #[stable(feature = "arc_unique", since = "1.4.0")] - pub fn get_mut(this: &mut Self) -> Option<&mut T> { - if this.is_unique() { - // This unsafety is ok because we're guaranteed that the pointer - // returned is the *only* pointer that will ever be returned to T. Our - // reference count is guaranteed to be 1 at this point, and we required - // the Arc itself to be `mut`, so we're returning the only possible - // reference to the inner data. - unsafe { - Some(&mut this.ptr.as_mut().data) - } - } else { - None - } - } - - /// Determine whether this is the unique reference (including weak refs) to - /// the underlying data. - /// - /// Note that this requires locking the weak ref count. - fn is_unique(&mut self) -> bool { - // lock the weak pointer count if we appear to be the sole weak pointer - // holder. - // - // The acquire label here ensures a happens-before relationship with any - // writes to `strong` prior to decrements of the `weak` count (via drop, - // which uses Release). - if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - // Due to the previous acquire read, this will observe any writes to - // `strong` that were due to upgrading weak pointers; only strong - // clones remain, which require that the strong count is > 1 anyway. - let unique = self.inner().strong.load(Relaxed) == 1; - - // The release write here synchronizes with a read in `downgrade`, - // effectively preventing the above read of `strong` from happening - // after the write. - self.inner().weak.store(1, Release); // release the lock - unique - } else { - false - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { - /// Drops the `Arc`. - /// - /// This will decrement the strong reference count. If the strong reference - /// count reaches zero then the only other references (if any) are - /// [`Weak`][weak], so we `drop` the inner value. - /// - /// [weak]: struct.Weak.html - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let foo2 = Arc::clone(&foo); - /// - /// drop(foo); // Doesn't print anything - /// drop(foo2); // Prints "dropped!" - /// ``` - #[inline] - fn drop(&mut self) { - // Because `fetch_sub` is already atomic, we do not need to synchronize - // with other threads unless we are going to delete the object. This - // same logic applies to the below `fetch_sub` to the `weak` count. - if self.inner().strong.fetch_sub(1, Release) != 1 { - return; - } - - // This fence is needed to prevent reordering of use of the data and - // deletion of the data. Because it is marked `Release`, the decreasing - // of the reference count synchronizes with this `Acquire` fence. This - // means that use of the data happens before decreasing the reference - // count, which happens before this fence, which happens before the - // deletion of the data. - // - // As explained in the [Boost documentation][1], - // - // > It is important to enforce any possible access to the object in one - // > thread (through an existing reference) to *happen before* deleting - // > the object in a different thread. This is achieved by a "release" - // > operation after dropping a reference (any access to the object - // > through this reference must obviously happened before), and an - // > "acquire" operation before deleting the object. - // - // In particular, while the contents of an Arc are usually immutable, it's - // possible to have interior writes to something like a Mutex. Since a - // Mutex is not acquired when it is deleted, we can't rely on its - // synchronization logic to make writes in thread A visible to a destructor - // running in thread B. - // - // Also note that the Acquire fence here could probably be replaced with an - // Acquire load, which could improve performance in highly-contended - // situations. See [2]. - // - // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) - // [2]: (https://github.com/rust-lang/rust/pull/41714) - atomic::fence(Acquire); - - unsafe { - self.drop_slow(); - } - } -} - -impl Arc { - #[inline] - #[unstable(feature = "rc_downcast", issue = "44608")] - /// Attempt to downcast the `Arc` to a concrete type. - /// - /// # Examples - /// - /// ``` - /// #![feature(rc_downcast)] - /// use std::any::Any; - /// use std::sync::Arc; - /// - /// fn print_if_string(value: Arc) { - /// if let Ok(string) = value.downcast::() { - /// println!("String ({}): {}", string.len(), string); - /// } - /// } - /// - /// fn main() { - /// let my_string = "Hello World".to_string(); - /// print_if_string(Arc::new(my_string)); - /// print_if_string(Arc::new(0i8)); - /// } - /// ``` - pub fn downcast(self) -> Result, Self> - where - T: Any + Send + Sync + 'static, - { - if (*self).is::() { - let ptr = self.ptr.cast::>(); - mem::forget(self); - Ok(Arc { ptr, phantom: PhantomData }) - } else { - Err(self) - } - } -} - -impl Weak { - /// Constructs a new `Weak`, without allocating any memory. - /// Calling [`upgrade`] on the return value always gives [`None`]. - /// - /// [`upgrade`]: struct.Weak.html#method.upgrade - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Weak::new(); - /// assert!(empty.upgrade().is_none()); - /// ``` - #[stable(feature = "downgraded_weak", since = "1.10.0")] - pub fn new() -> Weak { - unsafe { - Weak { - ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _), - } - } - } -} - -impl Weak { - /// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending - /// the lifetime of the value if successful. - /// - /// Returns [`None`] if the value has since been dropped. - /// - /// [`Arc`]: struct.Arc.html - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// let weak_five = Arc::downgrade(&five); - /// - /// let strong_five: Option> = weak_five.upgrade(); - /// assert!(strong_five.is_some()); - /// - /// // Destroy all strong pointers. - /// drop(strong_five); - /// drop(five); - /// - /// assert!(weak_five.upgrade().is_none()); - /// ``` - #[stable(feature = "arc_weak", since = "1.4.0")] - pub fn upgrade(&self) -> Option> { - // We use a CAS loop to increment the strong count instead of a - // fetch_add because once the count hits 0 it must never be above 0. - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return None; - } else { - unsafe { self.ptr.as_ref() } - }; - - // Relaxed load because any write of 0 that we can observe - // leaves the field in a permanently zero state (so a - // "stale" read of 0 is fine), and any other value is - // confirmed via the CAS below. - let mut n = inner.strong.load(Relaxed); - - loop { - if n == 0 { - return None; - } - - // See comments in `Arc::clone` for why we do this (for `mem::forget`). - if n > MAX_REFCOUNT { - unsafe { - abort(); - } - } - - // Relaxed is valid for the same reason it is on Arc's Clone impl - match inner.strong.compare_exchange_weak(n, n + 1, Relaxed, Relaxed) { - Ok(_) => return Some(Arc { - // null checked above - ptr: self.ptr, - phantom: PhantomData, - }), - Err(old) => n = old, - } - } - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl Clone for Weak { - /// Makes a clone of the `Weak` pointer that points to the same value. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// let weak_five = Arc::downgrade(&Arc::new(5)); - /// - /// Weak::clone(&weak_five); - /// ``` - #[inline] - fn clone(&self) -> Weak { - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return Weak { ptr: self.ptr }; - } else { - unsafe { self.ptr.as_ref() } - }; - // See comments in Arc::clone() for why this is relaxed. This can use a - // fetch_add (ignoring the lock) because the weak count is only locked - // where are *no other* weak pointers in existence. (So we can't be - // running this code in that case). - let old_size = inner.weak.fetch_add(1, Relaxed); - - // See comments in Arc::clone() for why we do this (for mem::forget). - if old_size > MAX_REFCOUNT { - unsafe { - abort(); - } - } - - return Weak { ptr: self.ptr }; - } -} - -#[stable(feature = "downgraded_weak", since = "1.10.0")] -impl Default for Weak { - /// Constructs a new `Weak`, without allocating memory. - /// Calling [`upgrade`] on the return value always gives [`None`]. - /// - /// [`upgrade`]: struct.Weak.html#method.upgrade - /// [`None`]: ../../std/option/enum.Option.html#variant.None - /// - /// # Examples - /// - /// ``` - /// use std::sync::Weak; - /// - /// let empty: Weak = Default::default(); - /// assert!(empty.upgrade().is_none()); - /// ``` - fn default() -> Weak { - Weak::new() - } -} - -#[stable(feature = "arc_weak", since = "1.4.0")] -impl Drop for Weak { - /// Drops the `Weak` pointer. - /// - /// # Examples - /// - /// ``` - /// use std::sync::{Arc, Weak}; - /// - /// struct Foo; - /// - /// impl Drop for Foo { - /// fn drop(&mut self) { - /// println!("dropped!"); - /// } - /// } - /// - /// let foo = Arc::new(Foo); - /// let weak_foo = Arc::downgrade(&foo); - /// let other_weak_foo = Weak::clone(&weak_foo); - /// - /// drop(weak_foo); // Doesn't print anything - /// drop(foo); // Prints "dropped!" - /// - /// assert!(other_weak_foo.upgrade().is_none()); - /// ``` - fn drop(&mut self) { - // If we find out that we were the last weak pointer, then its time to - // deallocate the data entirely. See the discussion in Arc::drop() about - // the memory orderings - // - // It's not necessary to check for the locked state here, because the - // weak count can only be locked if there was precisely one weak ref, - // meaning that drop could only subsequently run ON that remaining weak - // ref, which can only happen after the lock is released. - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return; - } else { - unsafe { self.ptr.as_ref() } - }; - - if inner.weak.fetch_sub(1, Release) == 1 { - atomic::fence(Acquire); - unsafe { - Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) - } - } - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialEq for Arc { - /// Equality for two `Arc`s. - /// - /// Two `Arc`s are equal if their inner values are equal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five == Arc::new(5)); - /// ``` - fn eq(&self, other: &Arc) -> bool { - *(*self) == *(*other) - } - - /// Inequality for two `Arc`s. - /// - /// Two `Arc`s are unequal if their inner values are unequal. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five != Arc::new(6)); - /// ``` - fn ne(&self, other: &Arc) -> bool { - *(*self) != *(*other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl PartialOrd for Arc { - /// Partial comparison for two `Arc`s. - /// - /// The two are compared by calling `partial_cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); - /// ``` - fn partial_cmp(&self, other: &Arc) -> Option { - (**self).partial_cmp(&**other) - } - - /// Less-than comparison for two `Arc`s. - /// - /// The two are compared by calling `<` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five < Arc::new(6)); - /// ``` - fn lt(&self, other: &Arc) -> bool { - *(*self) < *(*other) - } - - /// 'Less than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `<=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five <= Arc::new(5)); - /// ``` - fn le(&self, other: &Arc) -> bool { - *(*self) <= *(*other) - } - - /// Greater-than comparison for two `Arc`s. - /// - /// The two are compared by calling `>` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five > Arc::new(4)); - /// ``` - fn gt(&self, other: &Arc) -> bool { - *(*self) > *(*other) - } - - /// 'Greater than or equal to' comparison for two `Arc`s. - /// - /// The two are compared by calling `>=` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let five = Arc::new(5); - /// - /// assert!(five >= Arc::new(5)); - /// ``` - fn ge(&self, other: &Arc) -> bool { - *(*self) >= *(*other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Ord for Arc { - /// Comparison for two `Arc`s. - /// - /// The two are compared by calling `cmp()` on their inner values. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// use std::cmp::Ordering; - /// - /// let five = Arc::new(5); - /// - /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); - /// ``` - fn cmp(&self, other: &Arc) -> Ordering { - (**self).cmp(&**other) - } -} -#[stable(feature = "rust1", since = "1.0.0")] -impl Eq for Arc {} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Display for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Display::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Debug for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Debug::fmt(&**self, f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl fmt::Pointer for Arc { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - fmt::Pointer::fmt(&(&**self as *const T), f) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Default for Arc { - /// Creates a new `Arc`, with the `Default` value for `T`. - /// - /// # Examples - /// - /// ``` - /// use std::sync::Arc; - /// - /// let x: Arc = Default::default(); - /// assert_eq!(*x, 0); - /// ``` - fn default() -> Arc { - Arc::new(Default::default()) - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl Hash for Arc { - fn hash(&self, state: &mut H) { - (**self).hash(state) - } -} - -#[stable(feature = "from_for_ptrs", since = "1.6.0")] -impl From for Arc { - fn from(t: T) -> Self { - Arc::new(t) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl<'a, T: Clone> From<&'a [T]> for Arc<[T]> { - #[inline] - fn from(v: &[T]) -> Arc<[T]> { - >::from_slice(v) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl<'a> From<&'a str> for Arc { - #[inline] - fn from(v: &str) -> Arc { - let arc = Arc::<[u8]>::from(v.as_bytes()); - unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From for Arc { - #[inline] - fn from(v: String) -> Arc { - Arc::from(&v[..]) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc { - #[inline] - fn from(v: Box) -> Arc { - Arc::from_box(v) - } -} - -#[stable(feature = "shared_from_slice", since = "1.21.0")] -impl From> for Arc<[T]> { - #[inline] - fn from(mut v: Vec) -> Arc<[T]> { - unsafe { - let arc = Arc::copy_from_slice(&v); - - // Allow the Vec to free its memory, but not destroy its contents - v.set_len(0); - - arc - } - } -} - -#[cfg(test)] -mod tests { - use std::boxed::Box; - use std::clone::Clone; - use std::sync::mpsc::channel; - use std::mem::drop; - use std::ops::Drop; - use std::option::Option; - use std::option::Option::{None, Some}; - use std::sync::atomic; - use std::sync::atomic::Ordering::{Acquire, SeqCst}; - use std::thread; - use std::sync::Mutex; - use std::convert::From; - - use super::{Arc, Weak}; - use vec::Vec; - - struct Canary(*mut atomic::AtomicUsize); - - impl Drop for Canary { - fn drop(&mut self) { - unsafe { - match *self { - Canary(c) => { - (*c).fetch_add(1, SeqCst); - } - } - } - } - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn manually_share_arc() { - let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; - let arc_v = Arc::new(v); - - let (tx, rx) = channel(); - - let _t = thread::spawn(move || { - let arc_v: Arc> = rx.recv().unwrap(); - assert_eq!((*arc_v)[3], 4); - }); - - tx.send(arc_v.clone()).unwrap(); - - assert_eq!((*arc_v)[2], 3); - assert_eq!((*arc_v)[4], 5); - } - - #[test] - fn test_arc_get_mut() { - let mut x = Arc::new(3); - *Arc::get_mut(&mut x).unwrap() = 4; - assert_eq!(*x, 4); - let y = x.clone(); - assert!(Arc::get_mut(&mut x).is_none()); - drop(y); - assert!(Arc::get_mut(&mut x).is_some()); - let _w = Arc::downgrade(&x); - assert!(Arc::get_mut(&mut x).is_none()); - } - - #[test] - fn try_unwrap() { - let x = Arc::new(3); - assert_eq!(Arc::try_unwrap(x), Ok(3)); - let x = Arc::new(4); - let _y = x.clone(); - assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); - let x = Arc::new(5); - let _w = Arc::downgrade(&x); - assert_eq!(Arc::try_unwrap(x), Ok(5)); - } - - #[test] - fn into_from_raw() { - let x = Arc::new(box "hello"); - let y = x.clone(); - - let x_ptr = Arc::into_raw(x); - drop(y); - unsafe { - assert_eq!(**x_ptr, "hello"); - - let x = Arc::from_raw(x_ptr); - assert_eq!(**x, "hello"); - - assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello")); - } - } - - #[test] - fn test_into_from_raw_unsized() { - use std::fmt::Display; - use std::string::ToString; - - let arc: Arc = Arc::from("foo"); - - let ptr = Arc::into_raw(arc.clone()); - let arc2 = unsafe { Arc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }, "foo"); - assert_eq!(arc, arc2); - - let arc: Arc = Arc::new(123); - - let ptr = Arc::into_raw(arc.clone()); - let arc2 = unsafe { Arc::from_raw(ptr) }; - - assert_eq!(unsafe { &*ptr }.to_string(), "123"); - assert_eq!(arc2.to_string(), "123"); - } - - #[test] - fn test_cowarc_clone_make_mut() { - let mut cow0 = Arc::new(75); - let mut cow1 = cow0.clone(); - let mut cow2 = cow1.clone(); - - assert!(75 == *Arc::make_mut(&mut cow0)); - assert!(75 == *Arc::make_mut(&mut cow1)); - assert!(75 == *Arc::make_mut(&mut cow2)); - - *Arc::make_mut(&mut cow0) += 1; - *Arc::make_mut(&mut cow1) += 2; - *Arc::make_mut(&mut cow2) += 3; - - assert!(76 == *cow0); - assert!(77 == *cow1); - assert!(78 == *cow2); - - // none should point to the same backing memory - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 != *cow2); - } - - #[test] - fn test_cowarc_clone_unique2() { - let mut cow0 = Arc::new(75); - let cow1 = cow0.clone(); - let cow2 = cow1.clone(); - - assert!(75 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - *Arc::make_mut(&mut cow0) += 1; - assert!(76 == *cow0); - assert!(75 == *cow1); - assert!(75 == *cow2); - - // cow1 and cow2 should share the same contents - // cow0 should have a unique reference - assert!(*cow0 != *cow1); - assert!(*cow0 != *cow2); - assert!(*cow1 == *cow2); - } - - #[test] - fn test_cowarc_clone_weak() { - let mut cow0 = Arc::new(75); - let cow1_weak = Arc::downgrade(&cow0); - - assert!(75 == *cow0); - assert!(75 == *cow1_weak.upgrade().unwrap()); - - *Arc::make_mut(&mut cow0) += 1; - - assert!(76 == *cow0); - assert!(cow1_weak.upgrade().is_none()); - } - - #[test] - fn test_live() { - let x = Arc::new(5); - let y = Arc::downgrade(&x); - assert!(y.upgrade().is_some()); - } - - #[test] - fn test_dead() { - let x = Arc::new(5); - let y = Arc::downgrade(&x); - drop(x); - assert!(y.upgrade().is_none()); - } - - #[test] - fn weak_self_cyclic() { - struct Cycle { - x: Mutex>>, - } - - let a = Arc::new(Cycle { x: Mutex::new(None) }); - let b = Arc::downgrade(&a.clone()); - *a.x.lock().unwrap() = Some(b); - - // hopefully we don't double-free (or leak)... - } - - #[test] - fn drop_arc() { - let mut canary = atomic::AtomicUsize::new(0); - let x = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); - drop(x); - assert!(canary.load(Acquire) == 1); - } - - #[test] - fn drop_arc_weak() { - let mut canary = atomic::AtomicUsize::new(0); - let arc = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); - let arc_weak = Arc::downgrade(&arc); - assert!(canary.load(Acquire) == 0); - drop(arc); - assert!(canary.load(Acquire) == 1); - drop(arc_weak); - } - - #[test] - fn test_strong_count() { - let a = Arc::new(0); - assert!(Arc::strong_count(&a) == 1); - let w = Arc::downgrade(&a); - assert!(Arc::strong_count(&a) == 1); - let b = w.upgrade().expect(""); - assert!(Arc::strong_count(&b) == 2); - assert!(Arc::strong_count(&a) == 2); - drop(w); - drop(a); - assert!(Arc::strong_count(&b) == 1); - let c = b.clone(); - assert!(Arc::strong_count(&b) == 2); - assert!(Arc::strong_count(&c) == 2); - } - - #[test] - fn test_weak_count() { - let a = Arc::new(0); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 0); - let w = Arc::downgrade(&a); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 1); - let x = w.clone(); - assert!(Arc::weak_count(&a) == 2); - drop(w); - drop(x); - assert!(Arc::strong_count(&a) == 1); - assert!(Arc::weak_count(&a) == 0); - let c = a.clone(); - assert!(Arc::strong_count(&a) == 2); - assert!(Arc::weak_count(&a) == 0); - let d = Arc::downgrade(&c); - assert!(Arc::weak_count(&c) == 1); - assert!(Arc::strong_count(&c) == 2); - - drop(a); - drop(c); - drop(d); - } - - #[test] - fn show_arc() { - let a = Arc::new(5); - assert_eq!(format!("{:?}", a), "5"); - } - - // Make sure deriving works with Arc - #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)] - struct Foo { - inner: Arc, - } - - #[test] - fn test_unsized() { - let x: Arc<[i32]> = Arc::new([1, 2, 3]); - assert_eq!(format!("{:?}", x), "[1, 2, 3]"); - let y = Arc::downgrade(&x.clone()); - drop(x); - assert!(y.upgrade().is_none()); - } - - #[test] - fn test_from_owned() { - let foo = 123; - let foo_arc = Arc::from(foo); - assert!(123 == *foo_arc); - } - - #[test] - fn test_new_weak() { - let foo: Weak = Weak::new(); - assert!(foo.upgrade().is_none()); - } - - #[test] - fn test_ptr_eq() { - let five = Arc::new(5); - let same_five = five.clone(); - let other_five = Arc::new(5); - - assert!(Arc::ptr_eq(&five, &same_five)); - assert!(!Arc::ptr_eq(&five, &other_five)); - } - - #[test] - #[cfg_attr(target_os = "emscripten", ignore)] - fn test_weak_count_locked() { - let mut a = Arc::new(atomic::AtomicBool::new(false)); - let a2 = a.clone(); - let t = thread::spawn(move || { - for _i in 0..1000000 { - Arc::get_mut(&mut a); - } - a.store(true, SeqCst); - }); - - while !a2.load(SeqCst) { - let n = Arc::weak_count(&a2); - assert!(n < 2, "bad weak count: {}", n); - } - t.join().unwrap(); - } - - #[test] - fn test_from_str() { - let r: Arc = Arc::from("foo"); - - assert_eq!(&r[..], "foo"); - } - - #[test] - fn test_copy_from_slice() { - let s: &[u32] = &[1, 2, 3]; - let r: Arc<[u32]> = Arc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); - } - - #[test] - fn test_clone_from_slice() { - #[derive(Clone, Debug, Eq, PartialEq)] - struct X(u32); - - let s: &[X] = &[X(1), X(2), X(3)]; - let r: Arc<[X]> = Arc::from(s); - - assert_eq!(&r[..], s); - } - - #[test] - #[should_panic] - fn test_clone_from_slice_panic() { - use std::string::{String, ToString}; - - struct Fail(u32, String); - - impl Clone for Fail { - fn clone(&self) -> Fail { - if self.0 == 2 { - panic!(); - } - Fail(self.0, self.1.clone()) - } - } - - let s: &[Fail] = &[ - Fail(0, "foo".to_string()), - Fail(1, "bar".to_string()), - Fail(2, "baz".to_string()), - ]; - - // Should panic, but not cause memory corruption - let _r: Arc<[Fail]> = Arc::from(s); - } - - #[test] - fn test_from_box() { - let b: Box = box 123; - let r: Arc = Arc::from(b); - - assert_eq!(*r, 123); - } - - #[test] - fn test_from_box_str() { - use std::string::String; - - let s = String::from("foo").into_boxed_str(); - let r: Arc = Arc::from(s); - - assert_eq!(&r[..], "foo"); - } - - #[test] - fn test_from_box_slice() { - let s = vec![1, 2, 3].into_boxed_slice(); - let r: Arc<[u32]> = Arc::from(s); - - assert_eq!(&r[..], [1, 2, 3]); - } - - #[test] - fn test_from_box_trait() { - use std::fmt::Display; - use std::string::ToString; - - let b: Box = box 123; - let r: Arc = Arc::from(b); - - assert_eq!(r.to_string(), "123"); - } - - #[test] - fn test_from_box_trait_zero_sized() { - use std::fmt::Debug; - - let b: Box = box (); - let r: Arc = Arc::from(b); - - assert_eq!(format!("{:?}", r), "()"); - } - - #[test] - fn test_from_vec() { - let v = vec![1, 2, 3]; - let r: Arc<[u32]> = Arc::from(v); - - assert_eq!(&r[..], [1, 2, 3]); - } - - #[test] - fn test_downcast() { - use std::any::Any; - - let r1: Arc = Arc::new(i32::max_value()); - let r2: Arc = Arc::new("abc"); - - assert!(r1.clone().downcast::().is_err()); - - let r1i32 = r1.downcast::(); - assert!(r1i32.is_ok()); - assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); - - assert!(r2.clone().downcast::().is_err()); - - let r2str = r2.downcast::<&'static str>(); - assert!(r2str.is_ok()); - assert_eq!(r2str.unwrap(), Arc::new("abc")); - } -} - -#[stable(feature = "rust1", since = "1.0.0")] -impl borrow::Borrow for Arc { - fn borrow(&self) -> &T { - &**self - } -} - -#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] -impl AsRef for Arc { - fn as_ref(&self) -> &T { - &**self - } -} diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 9e1740473fe..8ec5a9ed193 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -40,7 +40,7 @@ //! //! ## Atomically reference counted pointers //! -//! The [`Arc`](arc/index.html) type is the threadsafe equivalent of the `Rc` +//! The [`Arc`](sync/index.html) type is the threadsafe equivalent of the `Rc` //! type. It provides all the same functionality of `Rc`, except it requires //! that the contained type `T` is shareable. Additionally, `Arc` is itself //! sendable while `Rc` is not. @@ -164,7 +164,7 @@ mod boxed { mod boxed_test; pub mod collections; #[cfg(target_has_atomic = "ptr")] -pub mod arc; +pub mod sync; pub mod rc; pub mod raw_vec; diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs new file mode 100644 index 00000000000..2abd9c85c57 --- /dev/null +++ b/src/liballoc/sync.rs @@ -0,0 +1,1936 @@ +// Copyright 2012-2014 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![stable(feature = "rust1", since = "1.0.0")] + +//! Thread-safe reference-counting pointers. +//! +//! See the [`Arc`][arc] documentation for more details. +//! +//! [arc]: struct.Arc.html + +use core::any::Any; +use core::sync::atomic; +use core::sync::atomic::Ordering::{Acquire, Relaxed, Release, SeqCst}; +use core::borrow; +use core::fmt; +use core::cmp::Ordering; +use core::intrinsics::abort; +use core::mem::{self, align_of_val, size_of_val}; +use core::ops::Deref; +use core::ops::CoerceUnsized; +use core::ptr::{self, NonNull}; +use core::marker::{Unsize, PhantomData}; +use core::hash::{Hash, Hasher}; +use core::{isize, usize}; +use core::convert::From; + +use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; +use boxed::Box; +use string::String; +use vec::Vec; + +/// A soft limit on the amount of references that may be made to an `Arc`. +/// +/// Going above this limit will abort your program (although not +/// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. +const MAX_REFCOUNT: usize = (isize::MAX) as usize; + +/// A sentinel value that is used for the pointer of `Weak::new()`. +const WEAK_EMPTY: usize = 1; + +/// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically +/// Reference Counted'. +/// +/// The type `Arc` provides shared ownership of a value of type `T`, +/// allocated in the heap. Invoking [`clone`][clone] on `Arc` produces +/// a new pointer to the same value in the heap. When the last `Arc` +/// pointer to a given value is destroyed, the pointed-to value is +/// also destroyed. +/// +/// Shared references in Rust disallow mutation by default, and `Arc` is no +/// exception: you cannot generally obtain a mutable reference to something +/// inside an `Arc`. If you need to mutate through an `Arc`, use +/// [`Mutex`][mutex], [`RwLock`][rwlock], or one of the [`Atomic`][atomic] +/// types. +/// +/// ## Thread Safety +/// +/// Unlike [`Rc`], `Arc` uses atomic operations for its reference +/// counting. This means that it is thread-safe. The disadvantage is that +/// atomic operations are more expensive than ordinary memory accesses. If you +/// are not sharing reference-counted values between threads, consider using +/// [`Rc`] for lower overhead. [`Rc`] is a safe default, because the +/// compiler will catch any attempt to send an [`Rc`] between threads. +/// However, a library might choose `Arc` in order to give library consumers +/// more flexibility. +/// +/// `Arc` will implement [`Send`] and [`Sync`] as long as the `T` implements +/// [`Send`] and [`Sync`]. Why can't you put a non-thread-safe type `T` in an +/// `Arc` to make it thread-safe? This may be a bit counter-intuitive at +/// first: after all, isn't the point of `Arc` thread safety? The key is +/// this: `Arc` makes it thread safe to have multiple ownership of the same +/// data, but it doesn't add thread safety to its data. Consider +/// `Arc<`[`RefCell`]`>`. [`RefCell`] isn't [`Sync`], and if `Arc` was always +/// [`Send`], `Arc<`[`RefCell`]`>` would be as well. But then we'd have a problem: +/// [`RefCell`] is not thread safe; it keeps track of the borrowing count using +/// non-atomic operations. +/// +/// In the end, this means that you may need to pair `Arc` with some sort of +/// [`std::sync`] type, usually [`Mutex`][mutex]. +/// +/// ## Breaking cycles with `Weak` +/// +/// The [`downgrade`][downgrade] method can be used to create a non-owning +/// [`Weak`][weak] pointer. A [`Weak`][weak] pointer can be [`upgrade`][upgrade]d +/// to an `Arc`, but this will return [`None`] if the value has already been +/// dropped. +/// +/// A cycle between `Arc` pointers will never be deallocated. For this reason, +/// [`Weak`][weak] is used to break cycles. For example, a tree could have +/// strong `Arc` pointers from parent nodes to children, and [`Weak`][weak] +/// pointers from children back to their parents. +/// +/// # Cloning references +/// +/// Creating a new reference from an existing reference counted pointer is done using the +/// `Clone` trait implemented for [`Arc`][arc] and [`Weak`][weak]. +/// +/// ``` +/// use std::sync::Arc; +/// let foo = Arc::new(vec![1.0, 2.0, 3.0]); +/// // The two syntaxes below are equivalent. +/// let a = foo.clone(); +/// let b = Arc::clone(&foo); +/// // a and b both point to the same memory location as foo. +/// ``` +/// +/// The [`Arc::clone(&from)`] syntax is the most idiomatic because it conveys more explicitly +/// the meaning of the code. In the example above, this syntax makes it easier to see that +/// this code is creating a new reference rather than copying the whole content of foo. +/// +/// ## `Deref` behavior +/// +/// `Arc` automatically dereferences to `T` (via the [`Deref`][deref] trait), +/// so you can call `T`'s methods on a value of type `Arc`. To avoid name +/// clashes with `T`'s methods, the methods of `Arc` itself are [associated +/// functions][assoc], called using function-like syntax: +/// +/// ``` +/// use std::sync::Arc; +/// let my_arc = Arc::new(()); +/// +/// Arc::downgrade(&my_arc); +/// ``` +/// +/// [`Weak`][weak] does not auto-dereference to `T`, because the value may have +/// already been destroyed. +/// +/// [arc]: struct.Arc.html +/// [weak]: struct.Weak.html +/// [`Rc`]: ../../std/rc/struct.Rc.html +/// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone +/// [mutex]: ../../std/sync/struct.Mutex.html +/// [rwlock]: ../../std/sync/struct.RwLock.html +/// [atomic]: ../../std/sync/atomic/index.html +/// [`Send`]: ../../std/marker/trait.Send.html +/// [`Sync`]: ../../std/marker/trait.Sync.html +/// [deref]: ../../std/ops/trait.Deref.html +/// [downgrade]: struct.Arc.html#method.downgrade +/// [upgrade]: struct.Weak.html#method.upgrade +/// [`None`]: ../../std/option/enum.Option.html#variant.None +/// [assoc]: ../../book/first-edition/method-syntax.html#associated-functions +/// [`RefCell`]: ../../std/cell/struct.RefCell.html +/// [`std::sync`]: ../../std/sync/index.html +/// [`Arc::clone(&from)`]: #method.clone +/// +/// # Examples +/// +/// Sharing some immutable data between threads: +/// +// Note that we **do not** run these tests here. The windows builders get super +// unhappy if a thread outlives the main thread and then exits at the same time +// (something deadlocks) so we just avoid this entirely by not running these +// tests. +/// ```no_run +/// use std::sync::Arc; +/// use std::thread; +/// +/// let five = Arc::new(5); +/// +/// for _ in 0..10 { +/// let five = Arc::clone(&five); +/// +/// thread::spawn(move || { +/// println!("{:?}", five); +/// }); +/// } +/// ``` +/// +/// Sharing a mutable [`AtomicUsize`]: +/// +/// [`AtomicUsize`]: ../../std/sync/atomic/struct.AtomicUsize.html +/// +/// ```no_run +/// use std::sync::Arc; +/// use std::sync::atomic::{AtomicUsize, Ordering}; +/// use std::thread; +/// +/// let val = Arc::new(AtomicUsize::new(5)); +/// +/// for _ in 0..10 { +/// let val = Arc::clone(&val); +/// +/// thread::spawn(move || { +/// let v = val.fetch_add(1, Ordering::SeqCst); +/// println!("{:?}", v); +/// }); +/// } +/// ``` +/// +/// See the [`rc` documentation][rc_examples] for more examples of reference +/// counting in general. +/// +/// [rc_examples]: ../../std/rc/index.html#examples +#[stable(feature = "rust1", since = "1.0.0")] +pub struct Arc { + ptr: NonNull>, + phantom: PhantomData, +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Send for Arc {} +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl Sync for Arc {} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for Arc {} + +/// `Weak` is a version of [`Arc`] that holds a non-owning reference to the +/// managed value. The value is accessed by calling [`upgrade`] on the `Weak` +/// pointer, which returns an [`Option`]`<`[`Arc`]`>`. +/// +/// Since a `Weak` reference does not count towards ownership, it will not +/// prevent the inner value from being dropped, and `Weak` itself makes no +/// guarantees about the value still being present and may return [`None`] +/// when [`upgrade`]d. +/// +/// A `Weak` pointer is useful for keeping a temporary reference to the value +/// within [`Arc`] without extending its lifetime. It is also used to prevent +/// circular references between [`Arc`] pointers, since mutual owning references +/// would never allow either [`Arc`] to be dropped. For example, a tree could +/// have strong [`Arc`] pointers from parent nodes to children, and `Weak` +/// pointers from children back to their parents. +/// +/// The typical way to obtain a `Weak` pointer is to call [`Arc::downgrade`]. +/// +/// [`Arc`]: struct.Arc.html +/// [`Arc::downgrade`]: struct.Arc.html#method.downgrade +/// [`upgrade`]: struct.Weak.html#method.upgrade +/// [`Option`]: ../../std/option/enum.Option.html +/// [`None`]: ../../std/option/enum.Option.html#variant.None +#[stable(feature = "arc_weak", since = "1.4.0")] +pub struct Weak { + // This is a `NonNull` to allow optimizing the size of this type in enums, + // but it is actually not truly "non-null". A `Weak::new()` will set this + // to a sentinel value, instead of needing to allocate some space in the + // heap. + ptr: NonNull>, +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Send for Weak {} +#[stable(feature = "arc_weak", since = "1.4.0")] +unsafe impl Sync for Weak {} + +#[unstable(feature = "coerce_unsized", issue = "27732")] +impl, U: ?Sized> CoerceUnsized> for Weak {} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl fmt::Debug for Weak { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "(Weak)") + } +} + +struct ArcInner { + strong: atomic::AtomicUsize, + + // the value usize::MAX acts as a sentinel for temporarily "locking" the + // ability to upgrade weak pointers or downgrade strong ones; this is used + // to avoid races in `make_mut` and `get_mut`. + weak: atomic::AtomicUsize, + + data: T, +} + +unsafe impl Send for ArcInner {} +unsafe impl Sync for ArcInner {} + +impl Arc { + /// Constructs a new `Arc`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// ``` + #[inline] + #[stable(feature = "rust1", since = "1.0.0")] + pub fn new(data: T) -> Arc { + // Start the weak pointer count as 1 which is the weak pointer that's + // held by all the strong pointers (kinda), see std/rc.rs for more info + let x: Box<_> = box ArcInner { + strong: atomic::AtomicUsize::new(1), + weak: atomic::AtomicUsize::new(1), + data, + }; + Arc { ptr: Box::into_raw_non_null(x), phantom: PhantomData } + } + + /// Returns the contained value, if the `Arc` has exactly one strong reference. + /// + /// Otherwise, an [`Err`][result] is returned with the same `Arc` that was + /// passed in. + /// + /// This will succeed even if there are outstanding weak references. + /// + /// [result]: ../../std/result/enum.Result.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(3); + /// assert_eq!(Arc::try_unwrap(x), Ok(3)); + /// + /// let x = Arc::new(4); + /// let _y = Arc::clone(&x); + /// assert_eq!(*Arc::try_unwrap(x).unwrap_err(), 4); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn try_unwrap(this: Self) -> Result { + // See `drop` for why all these atomics are like this + if this.inner().strong.compare_exchange(1, 0, Release, Relaxed).is_err() { + return Err(this); + } + + atomic::fence(Acquire); + + unsafe { + let elem = ptr::read(&this.ptr.as_ref().data); + + // Make a weak pointer to clean up the implicit strong-weak reference + let _weak = Weak { ptr: this.ptr }; + mem::forget(this); + + Ok(elem) + } + } +} + +impl Arc { + /// Consumes the `Arc`, returning the wrapped pointer. + /// + /// To avoid a memory leak the pointer must be converted back to an `Arc` using + /// [`Arc::from_raw`][from_raw]. + /// + /// [from_raw]: struct.Arc.html#method.from_raw + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(10); + /// let x_ptr = Arc::into_raw(x); + /// assert_eq!(unsafe { *x_ptr }, 10); + /// ``` + #[stable(feature = "rc_raw", since = "1.17.0")] + pub fn into_raw(this: Self) -> *const T { + let ptr: *const T = &*this; + mem::forget(this); + ptr + } + + /// Constructs an `Arc` from a raw pointer. + /// + /// The raw pointer must have been previously returned by a call to a + /// [`Arc::into_raw`][into_raw]. + /// + /// This function is unsafe because improper use may lead to memory problems. For example, a + /// double-free may occur if the function is called twice on the same raw pointer. + /// + /// [into_raw]: struct.Arc.html#method.into_raw + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x = Arc::new(10); + /// let x_ptr = Arc::into_raw(x); + /// + /// unsafe { + /// // Convert back to an `Arc` to prevent leak. + /// let x = Arc::from_raw(x_ptr); + /// assert_eq!(*x, 10); + /// + /// // Further calls to `Arc::from_raw(x_ptr)` would be memory unsafe. + /// } + /// + /// // The memory was freed when `x` went out of scope above, so `x_ptr` is now dangling! + /// ``` + #[stable(feature = "rc_raw", since = "1.17.0")] + pub unsafe fn from_raw(ptr: *const T) -> Self { + // Align the unsized value to the end of the ArcInner. + // Because it is ?Sized, it will always be the last field in memory. + let align = align_of_val(&*ptr); + let layout = Layout::new::>(); + let offset = (layout.size() + layout.padding_needed_for(align)) as isize; + + // Reverse the offset to find the original ArcInner. + let fake_ptr = ptr as *mut ArcInner; + let arc_ptr = set_data_ptr(fake_ptr, (ptr as *mut u8).offset(-offset)); + + Arc { + ptr: NonNull::new_unchecked(arc_ptr), + phantom: PhantomData, + } + } + + /// Creates a new [`Weak`][weak] pointer to this value. + /// + /// [weak]: struct.Weak.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// ``` + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn downgrade(this: &Self) -> Weak { + // This Relaxed is OK because we're checking the value in the CAS + // below. + let mut cur = this.inner().weak.load(Relaxed); + + loop { + // check if the weak counter is currently "locked"; if so, spin. + if cur == usize::MAX { + cur = this.inner().weak.load(Relaxed); + continue; + } + + // NOTE: this code currently ignores the possibility of overflow + // into usize::MAX; in general both Rc and Arc need to be adjusted + // to deal with overflow. + + // Unlike with Clone(), we need this to be an Acquire read to + // synchronize with the write coming from `is_unique`, so that the + // events prior to that write happen before this read. + match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { + Ok(_) => return Weak { ptr: this.ptr }, + Err(old) => cur = old, + } + } + } + + /// Gets the number of [`Weak`][weak] pointers to this value. + /// + /// [weak]: struct.Weak.html + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the weak count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _weak_five = Arc::downgrade(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` or `Weak` between threads. + /// assert_eq!(1, Arc::weak_count(&five)); + /// ``` + #[inline] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn weak_count(this: &Self) -> usize { + let cnt = this.inner().weak.load(SeqCst); + // If the weak count is currently locked, the value of the + // count was 0 just before taking the lock. + if cnt == usize::MAX { 0 } else { cnt - 1 } + } + + /// Gets the number of strong (`Arc`) pointers to this value. + /// + /// # Safety + /// + /// This method by itself is safe, but using it correctly requires extra care. + /// Another thread can change the strong count at any time, + /// including potentially between calling this method and acting on the result. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let _also_five = Arc::clone(&five); + /// + /// // This assertion is deterministic because we haven't shared + /// // the `Arc` between threads. + /// assert_eq!(2, Arc::strong_count(&five)); + /// ``` + #[inline] + #[stable(feature = "arc_counts", since = "1.15.0")] + pub fn strong_count(this: &Self) -> usize { + this.inner().strong.load(SeqCst) + } + + #[inline] + fn inner(&self) -> &ArcInner { + // This unsafety is ok because while this arc is alive we're guaranteed + // that the inner pointer is valid. Furthermore, we know that the + // `ArcInner` structure itself is `Sync` because the inner data is + // `Sync` as well, so we're ok loaning out an immutable pointer to these + // contents. + unsafe { self.ptr.as_ref() } + } + + // Non-inlined part of `drop`. + #[inline(never)] + unsafe fn drop_slow(&mut self) { + // Destroy the data at this time, even though we may not free the box + // allocation itself (there may still be weak pointers lying around). + ptr::drop_in_place(&mut self.ptr.as_mut().data); + + if self.inner().weak.fetch_sub(1, Release) == 1 { + atomic::fence(Acquire); + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) + } + } + + #[inline] + #[stable(feature = "ptr_eq", since = "1.17.0")] + /// Returns true if the two `Arc`s point to the same value (not + /// just values that compare as equal). + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// let same_five = Arc::clone(&five); + /// let other_five = Arc::new(5); + /// + /// assert!(Arc::ptr_eq(&five, &same_five)); + /// assert!(!Arc::ptr_eq(&five, &other_five)); + /// ``` + pub fn ptr_eq(this: &Self, other: &Self) -> bool { + this.ptr.as_ptr() == other.ptr.as_ptr() + } +} + +impl Arc { + // Allocates an `ArcInner` with sufficient space for an unsized value + unsafe fn allocate_for_ptr(ptr: *const T) -> *mut ArcInner { + // Create a fake ArcInner to find allocation size and alignment + let fake_ptr = ptr as *mut ArcInner; + + let layout = Layout::for_value(&*fake_ptr); + + let mem = Global.alloc(layout) + .unwrap_or_else(|_| handle_alloc_error(layout)); + + // Initialize the real ArcInner + let inner = set_data_ptr(ptr as *mut T, mem.as_ptr() as *mut u8) as *mut ArcInner; + + ptr::write(&mut (*inner).strong, atomic::AtomicUsize::new(1)); + ptr::write(&mut (*inner).weak, atomic::AtomicUsize::new(1)); + + inner + } + + fn from_box(v: Box) -> Arc { + unsafe { + let box_unique = Box::into_unique(v); + let bptr = box_unique.as_ptr(); + + let value_size = size_of_val(&*bptr); + let ptr = Self::allocate_for_ptr(bptr); + + // Copy value as bytes + ptr::copy_nonoverlapping( + bptr as *const T as *const u8, + &mut (*ptr).data as *mut _ as *mut u8, + value_size); + + // Free the allocation without dropping its contents + box_free(box_unique); + + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } + } + } +} + +// Sets the data pointer of a `?Sized` raw pointer. +// +// For a slice/trait object, this sets the `data` field and leaves the rest +// unchanged. For a sized raw pointer, this simply sets the pointer. +unsafe fn set_data_ptr(mut ptr: *mut T, data: *mut U) -> *mut T { + ptr::write(&mut ptr as *mut _ as *mut *mut u8, data as *mut u8); + ptr +} + +impl Arc<[T]> { + // Copy elements from slice into newly allocated Arc<[T]> + // + // Unsafe because the caller must either take ownership or bind `T: Copy` + unsafe fn copy_from_slice(v: &[T]) -> Arc<[T]> { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + ptr::copy_nonoverlapping( + v.as_ptr(), + &mut (*ptr).data as *mut [T] as *mut T, + v.len()); + + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } + } +} + +// Specialization trait used for From<&[T]> +trait ArcFromSlice { + fn from_slice(slice: &[T]) -> Self; +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + default fn from_slice(v: &[T]) -> Self { + // Panic guard while cloning T elements. + // In the event of a panic, elements that have been written + // into the new ArcInner will be dropped, then the memory freed. + struct Guard { + mem: NonNull, + elems: *mut T, + layout: Layout, + n_elems: usize, + } + + impl Drop for Guard { + fn drop(&mut self) { + use core::slice::from_raw_parts_mut; + + unsafe { + let slice = from_raw_parts_mut(self.elems, self.n_elems); + ptr::drop_in_place(slice); + + Global.dealloc(self.mem.cast(), self.layout.clone()); + } + } + } + + unsafe { + let v_ptr = v as *const [T]; + let ptr = Self::allocate_for_ptr(v_ptr); + + let mem = ptr as *mut _ as *mut u8; + let layout = Layout::for_value(&*ptr); + + // Pointer to first element + let elems = &mut (*ptr).data as *mut [T] as *mut T; + + let mut guard = Guard{ + mem: NonNull::new_unchecked(mem), + elems: elems, + layout: layout, + n_elems: 0, + }; + + for (i, item) in v.iter().enumerate() { + ptr::write(elems.offset(i as isize), item.clone()); + guard.n_elems += 1; + } + + // All clear. Forget the guard so it doesn't free the new ArcInner. + mem::forget(guard); + + Arc { ptr: NonNull::new_unchecked(ptr), phantom: PhantomData } + } + } +} + +impl ArcFromSlice for Arc<[T]> { + #[inline] + fn from_slice(v: &[T]) -> Self { + unsafe { Arc::copy_from_slice(v) } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Clone for Arc { + /// Makes a clone of the `Arc` pointer. + /// + /// This creates another pointer to the same inner value, increasing the + /// strong reference count. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// Arc::clone(&five); + /// ``` + #[inline] + fn clone(&self) -> Arc { + // Using a relaxed ordering is alright here, as knowledge of the + // original reference prevents other threads from erroneously deleting + // the object. + // + // As explained in the [Boost documentation][1], Increasing the + // reference counter can always be done with memory_order_relaxed: New + // references to an object can only be formed from an existing + // reference, and passing an existing reference from one thread to + // another must already provide any required synchronization. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + let old_size = self.inner().strong.fetch_add(1, Relaxed); + + // However we need to guard against massive refcounts in case someone + // is `mem::forget`ing Arcs. If we don't do this the count can overflow + // and users will use-after free. We racily saturate to `isize::MAX` on + // the assumption that there aren't ~2 billion threads incrementing + // the reference count at once. This branch will never be taken in + // any realistic program. + // + // We abort because such a program is incredibly degenerate, and we + // don't care to support it. + if old_size > MAX_REFCOUNT { + unsafe { + abort(); + } + } + + Arc { ptr: self.ptr, phantom: PhantomData } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Deref for Arc { + type Target = T; + + #[inline] + fn deref(&self) -> &T { + &self.inner().data + } +} + +impl Arc { + /// Makes a mutable reference into the given `Arc`. + /// + /// If there are other `Arc` or [`Weak`][weak] pointers to the same value, + /// then `make_mut` will invoke [`clone`][clone] on the inner value to + /// ensure unique ownership. This is also referred to as clone-on-write. + /// + /// See also [`get_mut`][get_mut], which will fail rather than cloning. + /// + /// [weak]: struct.Weak.html + /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone + /// [get_mut]: struct.Arc.html#method.get_mut + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut data = Arc::new(5); + /// + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// let mut other_data = Arc::clone(&data); // Won't clone inner data + /// *Arc::make_mut(&mut data) += 1; // Clones inner data + /// *Arc::make_mut(&mut data) += 1; // Won't clone anything + /// *Arc::make_mut(&mut other_data) *= 2; // Won't clone anything + /// + /// // Now `data` and `other_data` point to different values. + /// assert_eq!(*data, 8); + /// assert_eq!(*other_data, 12); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn make_mut(this: &mut Self) -> &mut T { + // Note that we hold both a strong reference and a weak reference. + // Thus, releasing our strong reference only will not, by itself, cause + // the memory to be deallocated. + // + // Use Acquire to ensure that we see any writes to `weak` that happen + // before release writes (i.e., decrements) to `strong`. Since we hold a + // weak count, there's no chance the ArcInner itself could be + // deallocated. + if this.inner().strong.compare_exchange(1, 0, Acquire, Relaxed).is_err() { + // Another strong pointer exists; clone + *this = Arc::new((**this).clone()); + } else if this.inner().weak.load(Relaxed) != 1 { + // Relaxed suffices in the above because this is fundamentally an + // optimization: we are always racing with weak pointers being + // dropped. Worst case, we end up allocated a new Arc unnecessarily. + + // We removed the last strong ref, but there are additional weak + // refs remaining. We'll move the contents to a new Arc, and + // invalidate the other weak refs. + + // Note that it is not possible for the read of `weak` to yield + // usize::MAX (i.e., locked), since the weak count can only be + // locked by a thread with a strong reference. + + // Materialize our own implicit weak pointer, so that it can clean + // up the ArcInner as needed. + let weak = Weak { ptr: this.ptr }; + + // mark the data itself as already deallocated + unsafe { + // there is no data race in the implicit write caused by `read` + // here (due to zeroing) because data is no longer accessed by + // other threads (due to there being no more strong refs at this + // point). + let mut swap = Arc::new(ptr::read(&weak.ptr.as_ref().data)); + mem::swap(this, &mut swap); + mem::forget(swap); + } + } else { + // We were the sole reference of either kind; bump back up the + // strong ref count. + this.inner().strong.store(1, Release); + } + + // As with `get_mut()`, the unsafety is ok because our reference was + // either unique to begin with, or became one upon cloning the contents. + unsafe { + &mut this.ptr.as_mut().data + } + } +} + +impl Arc { + /// Returns a mutable reference to the inner value, if there are + /// no other `Arc` or [`Weak`][weak] pointers to the same value. + /// + /// Returns [`None`][option] otherwise, because it is not safe to + /// mutate a shared value. + /// + /// See also [`make_mut`][make_mut], which will [`clone`][clone] + /// the inner value when it's shared. + /// + /// [weak]: struct.Weak.html + /// [option]: ../../std/option/enum.Option.html + /// [make_mut]: struct.Arc.html#method.make_mut + /// [clone]: ../../std/clone/trait.Clone.html#tymethod.clone + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let mut x = Arc::new(3); + /// *Arc::get_mut(&mut x).unwrap() = 4; + /// assert_eq!(*x, 4); + /// + /// let _y = Arc::clone(&x); + /// assert!(Arc::get_mut(&mut x).is_none()); + /// ``` + #[inline] + #[stable(feature = "arc_unique", since = "1.4.0")] + pub fn get_mut(this: &mut Self) -> Option<&mut T> { + if this.is_unique() { + // This unsafety is ok because we're guaranteed that the pointer + // returned is the *only* pointer that will ever be returned to T. Our + // reference count is guaranteed to be 1 at this point, and we required + // the Arc itself to be `mut`, so we're returning the only possible + // reference to the inner data. + unsafe { + Some(&mut this.ptr.as_mut().data) + } + } else { + None + } + } + + /// Determine whether this is the unique reference (including weak refs) to + /// the underlying data. + /// + /// Note that this requires locking the weak ref count. + fn is_unique(&mut self) -> bool { + // lock the weak pointer count if we appear to be the sole weak pointer + // holder. + // + // The acquire label here ensures a happens-before relationship with any + // writes to `strong` prior to decrements of the `weak` count (via drop, + // which uses Release). + if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { + // Due to the previous acquire read, this will observe any writes to + // `strong` that were due to upgrading weak pointers; only strong + // clones remain, which require that the strong count is > 1 anyway. + let unique = self.inner().strong.load(Relaxed) == 1; + + // The release write here synchronizes with a read in `downgrade`, + // effectively preventing the above read of `strong` from happening + // after the write. + self.inner().weak.store(1, Release); // release the lock + unique + } else { + false + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { + /// Drops the `Arc`. + /// + /// This will decrement the strong reference count. If the strong reference + /// count reaches zero then the only other references (if any) are + /// [`Weak`][weak], so we `drop` the inner value. + /// + /// [weak]: struct.Weak.html + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let foo2 = Arc::clone(&foo); + /// + /// drop(foo); // Doesn't print anything + /// drop(foo2); // Prints "dropped!" + /// ``` + #[inline] + fn drop(&mut self) { + // Because `fetch_sub` is already atomic, we do not need to synchronize + // with other threads unless we are going to delete the object. This + // same logic applies to the below `fetch_sub` to the `weak` count. + if self.inner().strong.fetch_sub(1, Release) != 1 { + return; + } + + // This fence is needed to prevent reordering of use of the data and + // deletion of the data. Because it is marked `Release`, the decreasing + // of the reference count synchronizes with this `Acquire` fence. This + // means that use of the data happens before decreasing the reference + // count, which happens before this fence, which happens before the + // deletion of the data. + // + // As explained in the [Boost documentation][1], + // + // > It is important to enforce any possible access to the object in one + // > thread (through an existing reference) to *happen before* deleting + // > the object in a different thread. This is achieved by a "release" + // > operation after dropping a reference (any access to the object + // > through this reference must obviously happened before), and an + // > "acquire" operation before deleting the object. + // + // In particular, while the contents of an Arc are usually immutable, it's + // possible to have interior writes to something like a Mutex. Since a + // Mutex is not acquired when it is deleted, we can't rely on its + // synchronization logic to make writes in thread A visible to a destructor + // running in thread B. + // + // Also note that the Acquire fence here could probably be replaced with an + // Acquire load, which could improve performance in highly-contended + // situations. See [2]. + // + // [1]: (www.boost.org/doc/libs/1_55_0/doc/html/atomic/usage_examples.html) + // [2]: (https://github.com/rust-lang/rust/pull/41714) + atomic::fence(Acquire); + + unsafe { + self.drop_slow(); + } + } +} + +impl Arc { + #[inline] + #[unstable(feature = "rc_downcast", issue = "44608")] + /// Attempt to downcast the `Arc` to a concrete type. + /// + /// # Examples + /// + /// ``` + /// #![feature(rc_downcast)] + /// use std::any::Any; + /// use std::sync::Arc; + /// + /// fn print_if_string(value: Arc) { + /// if let Ok(string) = value.downcast::() { + /// println!("String ({}): {}", string.len(), string); + /// } + /// } + /// + /// fn main() { + /// let my_string = "Hello World".to_string(); + /// print_if_string(Arc::new(my_string)); + /// print_if_string(Arc::new(0i8)); + /// } + /// ``` + pub fn downcast(self) -> Result, Self> + where + T: Any + Send + Sync + 'static, + { + if (*self).is::() { + let ptr = self.ptr.cast::>(); + mem::forget(self); + Ok(Arc { ptr, phantom: PhantomData }) + } else { + Err(self) + } + } +} + +impl Weak { + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. + /// + /// [`upgrade`]: struct.Weak.html#method.upgrade + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Weak::new(); + /// assert!(empty.upgrade().is_none()); + /// ``` + #[stable(feature = "downgraded_weak", since = "1.10.0")] + pub fn new() -> Weak { + unsafe { + Weak { + ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _), + } + } + } +} + +impl Weak { + /// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending + /// the lifetime of the value if successful. + /// + /// Returns [`None`] if the value has since been dropped. + /// + /// [`Arc`]: struct.Arc.html + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// let weak_five = Arc::downgrade(&five); + /// + /// let strong_five: Option> = weak_five.upgrade(); + /// assert!(strong_five.is_some()); + /// + /// // Destroy all strong pointers. + /// drop(strong_five); + /// drop(five); + /// + /// assert!(weak_five.upgrade().is_none()); + /// ``` + #[stable(feature = "arc_weak", since = "1.4.0")] + pub fn upgrade(&self) -> Option> { + // We use a CAS loop to increment the strong count instead of a + // fetch_add because once the count hits 0 it must never be above 0. + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return None; + } else { + unsafe { self.ptr.as_ref() } + }; + + // Relaxed load because any write of 0 that we can observe + // leaves the field in a permanently zero state (so a + // "stale" read of 0 is fine), and any other value is + // confirmed via the CAS below. + let mut n = inner.strong.load(Relaxed); + + loop { + if n == 0 { + return None; + } + + // See comments in `Arc::clone` for why we do this (for `mem::forget`). + if n > MAX_REFCOUNT { + unsafe { + abort(); + } + } + + // Relaxed is valid for the same reason it is on Arc's Clone impl + match inner.strong.compare_exchange_weak(n, n + 1, Relaxed, Relaxed) { + Ok(_) => return Some(Arc { + // null checked above + ptr: self.ptr, + phantom: PhantomData, + }), + Err(old) => n = old, + } + } + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl Clone for Weak { + /// Makes a clone of the `Weak` pointer that points to the same value. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// let weak_five = Arc::downgrade(&Arc::new(5)); + /// + /// Weak::clone(&weak_five); + /// ``` + #[inline] + fn clone(&self) -> Weak { + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return Weak { ptr: self.ptr }; + } else { + unsafe { self.ptr.as_ref() } + }; + // See comments in Arc::clone() for why this is relaxed. This can use a + // fetch_add (ignoring the lock) because the weak count is only locked + // where are *no other* weak pointers in existence. (So we can't be + // running this code in that case). + let old_size = inner.weak.fetch_add(1, Relaxed); + + // See comments in Arc::clone() for why we do this (for mem::forget). + if old_size > MAX_REFCOUNT { + unsafe { + abort(); + } + } + + return Weak { ptr: self.ptr }; + } +} + +#[stable(feature = "downgraded_weak", since = "1.10.0")] +impl Default for Weak { + /// Constructs a new `Weak`, without allocating memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. + /// + /// [`upgrade`]: struct.Weak.html#method.upgrade + /// [`None`]: ../../std/option/enum.Option.html#variant.None + /// + /// # Examples + /// + /// ``` + /// use std::sync::Weak; + /// + /// let empty: Weak = Default::default(); + /// assert!(empty.upgrade().is_none()); + /// ``` + fn default() -> Weak { + Weak::new() + } +} + +#[stable(feature = "arc_weak", since = "1.4.0")] +impl Drop for Weak { + /// Drops the `Weak` pointer. + /// + /// # Examples + /// + /// ``` + /// use std::sync::{Arc, Weak}; + /// + /// struct Foo; + /// + /// impl Drop for Foo { + /// fn drop(&mut self) { + /// println!("dropped!"); + /// } + /// } + /// + /// let foo = Arc::new(Foo); + /// let weak_foo = Arc::downgrade(&foo); + /// let other_weak_foo = Weak::clone(&weak_foo); + /// + /// drop(weak_foo); // Doesn't print anything + /// drop(foo); // Prints "dropped!" + /// + /// assert!(other_weak_foo.upgrade().is_none()); + /// ``` + fn drop(&mut self) { + // If we find out that we were the last weak pointer, then its time to + // deallocate the data entirely. See the discussion in Arc::drop() about + // the memory orderings + // + // It's not necessary to check for the locked state here, because the + // weak count can only be locked if there was precisely one weak ref, + // meaning that drop could only subsequently run ON that remaining weak + // ref, which can only happen after the lock is released. + let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { + return; + } else { + unsafe { self.ptr.as_ref() } + }; + + if inner.weak.fetch_sub(1, Release) == 1 { + atomic::fence(Acquire); + unsafe { + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())) + } + } + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialEq for Arc { + /// Equality for two `Arc`s. + /// + /// Two `Arc`s are equal if their inner values are equal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five == Arc::new(5)); + /// ``` + fn eq(&self, other: &Arc) -> bool { + *(*self) == *(*other) + } + + /// Inequality for two `Arc`s. + /// + /// Two `Arc`s are unequal if their inner values are unequal. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five != Arc::new(6)); + /// ``` + fn ne(&self, other: &Arc) -> bool { + *(*self) != *(*other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl PartialOrd for Arc { + /// Partial comparison for two `Arc`s. + /// + /// The two are compared by calling `partial_cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Some(Ordering::Less), five.partial_cmp(&Arc::new(6))); + /// ``` + fn partial_cmp(&self, other: &Arc) -> Option { + (**self).partial_cmp(&**other) + } + + /// Less-than comparison for two `Arc`s. + /// + /// The two are compared by calling `<` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five < Arc::new(6)); + /// ``` + fn lt(&self, other: &Arc) -> bool { + *(*self) < *(*other) + } + + /// 'Less than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `<=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five <= Arc::new(5)); + /// ``` + fn le(&self, other: &Arc) -> bool { + *(*self) <= *(*other) + } + + /// Greater-than comparison for two `Arc`s. + /// + /// The two are compared by calling `>` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five > Arc::new(4)); + /// ``` + fn gt(&self, other: &Arc) -> bool { + *(*self) > *(*other) + } + + /// 'Greater than or equal to' comparison for two `Arc`s. + /// + /// The two are compared by calling `>=` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let five = Arc::new(5); + /// + /// assert!(five >= Arc::new(5)); + /// ``` + fn ge(&self, other: &Arc) -> bool { + *(*self) >= *(*other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Ord for Arc { + /// Comparison for two `Arc`s. + /// + /// The two are compared by calling `cmp()` on their inner values. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// use std::cmp::Ordering; + /// + /// let five = Arc::new(5); + /// + /// assert_eq!(Ordering::Less, five.cmp(&Arc::new(6))); + /// ``` + fn cmp(&self, other: &Arc) -> Ordering { + (**self).cmp(&**other) + } +} +#[stable(feature = "rust1", since = "1.0.0")] +impl Eq for Arc {} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Display for Arc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Display::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Debug for Arc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Debug::fmt(&**self, f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl fmt::Pointer for Arc { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fmt::Pointer::fmt(&(&**self as *const T), f) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Default for Arc { + /// Creates a new `Arc`, with the `Default` value for `T`. + /// + /// # Examples + /// + /// ``` + /// use std::sync::Arc; + /// + /// let x: Arc = Default::default(); + /// assert_eq!(*x, 0); + /// ``` + fn default() -> Arc { + Arc::new(Default::default()) + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl Hash for Arc { + fn hash(&self, state: &mut H) { + (**self).hash(state) + } +} + +#[stable(feature = "from_for_ptrs", since = "1.6.0")] +impl From for Arc { + fn from(t: T) -> Self { + Arc::new(t) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a, T: Clone> From<&'a [T]> for Arc<[T]> { + #[inline] + fn from(v: &[T]) -> Arc<[T]> { + >::from_slice(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl<'a> From<&'a str> for Arc { + #[inline] + fn from(v: &str) -> Arc { + let arc = Arc::<[u8]>::from(v.as_bytes()); + unsafe { Arc::from_raw(Arc::into_raw(arc) as *const str) } + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From for Arc { + #[inline] + fn from(v: String) -> Arc { + Arc::from(&v[..]) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc { + #[inline] + fn from(v: Box) -> Arc { + Arc::from_box(v) + } +} + +#[stable(feature = "shared_from_slice", since = "1.21.0")] +impl From> for Arc<[T]> { + #[inline] + fn from(mut v: Vec) -> Arc<[T]> { + unsafe { + let arc = Arc::copy_from_slice(&v); + + // Allow the Vec to free its memory, but not destroy its contents + v.set_len(0); + + arc + } + } +} + +#[cfg(test)] +mod tests { + use std::boxed::Box; + use std::clone::Clone; + use std::sync::mpsc::channel; + use std::mem::drop; + use std::ops::Drop; + use std::option::Option; + use std::option::Option::{None, Some}; + use std::sync::atomic; + use std::sync::atomic::Ordering::{Acquire, SeqCst}; + use std::thread; + use std::sync::Mutex; + use std::convert::From; + + use super::{Arc, Weak}; + use vec::Vec; + + struct Canary(*mut atomic::AtomicUsize); + + impl Drop for Canary { + fn drop(&mut self) { + unsafe { + match *self { + Canary(c) => { + (*c).fetch_add(1, SeqCst); + } + } + } + } + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn manually_share_arc() { + let v = vec![1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; + let arc_v = Arc::new(v); + + let (tx, rx) = channel(); + + let _t = thread::spawn(move || { + let arc_v: Arc> = rx.recv().unwrap(); + assert_eq!((*arc_v)[3], 4); + }); + + tx.send(arc_v.clone()).unwrap(); + + assert_eq!((*arc_v)[2], 3); + assert_eq!((*arc_v)[4], 5); + } + + #[test] + fn test_arc_get_mut() { + let mut x = Arc::new(3); + *Arc::get_mut(&mut x).unwrap() = 4; + assert_eq!(*x, 4); + let y = x.clone(); + assert!(Arc::get_mut(&mut x).is_none()); + drop(y); + assert!(Arc::get_mut(&mut x).is_some()); + let _w = Arc::downgrade(&x); + assert!(Arc::get_mut(&mut x).is_none()); + } + + #[test] + fn try_unwrap() { + let x = Arc::new(3); + assert_eq!(Arc::try_unwrap(x), Ok(3)); + let x = Arc::new(4); + let _y = x.clone(); + assert_eq!(Arc::try_unwrap(x), Err(Arc::new(4))); + let x = Arc::new(5); + let _w = Arc::downgrade(&x); + assert_eq!(Arc::try_unwrap(x), Ok(5)); + } + + #[test] + fn into_from_raw() { + let x = Arc::new(box "hello"); + let y = x.clone(); + + let x_ptr = Arc::into_raw(x); + drop(y); + unsafe { + assert_eq!(**x_ptr, "hello"); + + let x = Arc::from_raw(x_ptr); + assert_eq!(**x, "hello"); + + assert_eq!(Arc::try_unwrap(x).map(|x| *x), Ok("hello")); + } + } + + #[test] + fn test_into_from_raw_unsized() { + use std::fmt::Display; + use std::string::ToString; + + let arc: Arc = Arc::from("foo"); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }, "foo"); + assert_eq!(arc, arc2); + + let arc: Arc = Arc::new(123); + + let ptr = Arc::into_raw(arc.clone()); + let arc2 = unsafe { Arc::from_raw(ptr) }; + + assert_eq!(unsafe { &*ptr }.to_string(), "123"); + assert_eq!(arc2.to_string(), "123"); + } + + #[test] + fn test_cowarc_clone_make_mut() { + let mut cow0 = Arc::new(75); + let mut cow1 = cow0.clone(); + let mut cow2 = cow1.clone(); + + assert!(75 == *Arc::make_mut(&mut cow0)); + assert!(75 == *Arc::make_mut(&mut cow1)); + assert!(75 == *Arc::make_mut(&mut cow2)); + + *Arc::make_mut(&mut cow0) += 1; + *Arc::make_mut(&mut cow1) += 2; + *Arc::make_mut(&mut cow2) += 3; + + assert!(76 == *cow0); + assert!(77 == *cow1); + assert!(78 == *cow2); + + // none should point to the same backing memory + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 != *cow2); + } + + #[test] + fn test_cowarc_clone_unique2() { + let mut cow0 = Arc::new(75); + let cow1 = cow0.clone(); + let cow2 = cow1.clone(); + + assert!(75 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + *Arc::make_mut(&mut cow0) += 1; + assert!(76 == *cow0); + assert!(75 == *cow1); + assert!(75 == *cow2); + + // cow1 and cow2 should share the same contents + // cow0 should have a unique reference + assert!(*cow0 != *cow1); + assert!(*cow0 != *cow2); + assert!(*cow1 == *cow2); + } + + #[test] + fn test_cowarc_clone_weak() { + let mut cow0 = Arc::new(75); + let cow1_weak = Arc::downgrade(&cow0); + + assert!(75 == *cow0); + assert!(75 == *cow1_weak.upgrade().unwrap()); + + *Arc::make_mut(&mut cow0) += 1; + + assert!(76 == *cow0); + assert!(cow1_weak.upgrade().is_none()); + } + + #[test] + fn test_live() { + let x = Arc::new(5); + let y = Arc::downgrade(&x); + assert!(y.upgrade().is_some()); + } + + #[test] + fn test_dead() { + let x = Arc::new(5); + let y = Arc::downgrade(&x); + drop(x); + assert!(y.upgrade().is_none()); + } + + #[test] + fn weak_self_cyclic() { + struct Cycle { + x: Mutex>>, + } + + let a = Arc::new(Cycle { x: Mutex::new(None) }); + let b = Arc::downgrade(&a.clone()); + *a.x.lock().unwrap() = Some(b); + + // hopefully we don't double-free (or leak)... + } + + #[test] + fn drop_arc() { + let mut canary = atomic::AtomicUsize::new(0); + let x = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); + drop(x); + assert!(canary.load(Acquire) == 1); + } + + #[test] + fn drop_arc_weak() { + let mut canary = atomic::AtomicUsize::new(0); + let arc = Arc::new(Canary(&mut canary as *mut atomic::AtomicUsize)); + let arc_weak = Arc::downgrade(&arc); + assert!(canary.load(Acquire) == 0); + drop(arc); + assert!(canary.load(Acquire) == 1); + drop(arc_weak); + } + + #[test] + fn test_strong_count() { + let a = Arc::new(0); + assert!(Arc::strong_count(&a) == 1); + let w = Arc::downgrade(&a); + assert!(Arc::strong_count(&a) == 1); + let b = w.upgrade().expect(""); + assert!(Arc::strong_count(&b) == 2); + assert!(Arc::strong_count(&a) == 2); + drop(w); + drop(a); + assert!(Arc::strong_count(&b) == 1); + let c = b.clone(); + assert!(Arc::strong_count(&b) == 2); + assert!(Arc::strong_count(&c) == 2); + } + + #[test] + fn test_weak_count() { + let a = Arc::new(0); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 0); + let w = Arc::downgrade(&a); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 1); + let x = w.clone(); + assert!(Arc::weak_count(&a) == 2); + drop(w); + drop(x); + assert!(Arc::strong_count(&a) == 1); + assert!(Arc::weak_count(&a) == 0); + let c = a.clone(); + assert!(Arc::strong_count(&a) == 2); + assert!(Arc::weak_count(&a) == 0); + let d = Arc::downgrade(&c); + assert!(Arc::weak_count(&c) == 1); + assert!(Arc::strong_count(&c) == 2); + + drop(a); + drop(c); + drop(d); + } + + #[test] + fn show_arc() { + let a = Arc::new(5); + assert_eq!(format!("{:?}", a), "5"); + } + + // Make sure deriving works with Arc + #[derive(Eq, Ord, PartialEq, PartialOrd, Clone, Debug, Default)] + struct Foo { + inner: Arc, + } + + #[test] + fn test_unsized() { + let x: Arc<[i32]> = Arc::new([1, 2, 3]); + assert_eq!(format!("{:?}", x), "[1, 2, 3]"); + let y = Arc::downgrade(&x.clone()); + drop(x); + assert!(y.upgrade().is_none()); + } + + #[test] + fn test_from_owned() { + let foo = 123; + let foo_arc = Arc::from(foo); + assert!(123 == *foo_arc); + } + + #[test] + fn test_new_weak() { + let foo: Weak = Weak::new(); + assert!(foo.upgrade().is_none()); + } + + #[test] + fn test_ptr_eq() { + let five = Arc::new(5); + let same_five = five.clone(); + let other_five = Arc::new(5); + + assert!(Arc::ptr_eq(&five, &same_five)); + assert!(!Arc::ptr_eq(&five, &other_five)); + } + + #[test] + #[cfg_attr(target_os = "emscripten", ignore)] + fn test_weak_count_locked() { + let mut a = Arc::new(atomic::AtomicBool::new(false)); + let a2 = a.clone(); + let t = thread::spawn(move || { + for _i in 0..1000000 { + Arc::get_mut(&mut a); + } + a.store(true, SeqCst); + }); + + while !a2.load(SeqCst) { + let n = Arc::weak_count(&a2); + assert!(n < 2, "bad weak count: {}", n); + } + t.join().unwrap(); + } + + #[test] + fn test_from_str() { + let r: Arc = Arc::from("foo"); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_copy_from_slice() { + let s: &[u32] = &[1, 2, 3]; + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_clone_from_slice() { + #[derive(Clone, Debug, Eq, PartialEq)] + struct X(u32); + + let s: &[X] = &[X(1), X(2), X(3)]; + let r: Arc<[X]> = Arc::from(s); + + assert_eq!(&r[..], s); + } + + #[test] + #[should_panic] + fn test_clone_from_slice_panic() { + use std::string::{String, ToString}; + + struct Fail(u32, String); + + impl Clone for Fail { + fn clone(&self) -> Fail { + if self.0 == 2 { + panic!(); + } + Fail(self.0, self.1.clone()) + } + } + + let s: &[Fail] = &[ + Fail(0, "foo".to_string()), + Fail(1, "bar".to_string()), + Fail(2, "baz".to_string()), + ]; + + // Should panic, but not cause memory corruption + let _r: Arc<[Fail]> = Arc::from(s); + } + + #[test] + fn test_from_box() { + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(*r, 123); + } + + #[test] + fn test_from_box_str() { + use std::string::String; + + let s = String::from("foo").into_boxed_str(); + let r: Arc = Arc::from(s); + + assert_eq!(&r[..], "foo"); + } + + #[test] + fn test_from_box_slice() { + let s = vec![1, 2, 3].into_boxed_slice(); + let r: Arc<[u32]> = Arc::from(s); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_from_box_trait() { + use std::fmt::Display; + use std::string::ToString; + + let b: Box = box 123; + let r: Arc = Arc::from(b); + + assert_eq!(r.to_string(), "123"); + } + + #[test] + fn test_from_box_trait_zero_sized() { + use std::fmt::Debug; + + let b: Box = box (); + let r: Arc = Arc::from(b); + + assert_eq!(format!("{:?}", r), "()"); + } + + #[test] + fn test_from_vec() { + let v = vec![1, 2, 3]; + let r: Arc<[u32]> = Arc::from(v); + + assert_eq!(&r[..], [1, 2, 3]); + } + + #[test] + fn test_downcast() { + use std::any::Any; + + let r1: Arc = Arc::new(i32::max_value()); + let r2: Arc = Arc::new("abc"); + + assert!(r1.clone().downcast::().is_err()); + + let r1i32 = r1.downcast::(); + assert!(r1i32.is_ok()); + assert_eq!(r1i32.unwrap(), Arc::new(i32::max_value())); + + assert!(r2.clone().downcast::().is_err()); + + let r2str = r2.downcast::<&'static str>(); + assert!(r2str.is_ok()); + assert_eq!(r2str.unwrap(), Arc::new("abc")); + } +} + +#[stable(feature = "rust1", since = "1.0.0")] +impl borrow::Borrow for Arc { + fn borrow(&self) -> &T { + &**self + } +} + +#[stable(since = "1.5.0", feature = "smart_ptr_as_ref")] +impl AsRef for Arc { + fn as_ref(&self) -> &T { + &**self + } +} diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs index 7b1947b56b8..f14fe3a20da 100644 --- a/src/liballoc/task.rs +++ b/src/liballoc/task.rs @@ -18,10 +18,10 @@ pub use self::if_arc::*; #[cfg(target_has_atomic = "ptr")] mod if_arc { use super::*; - use arc::Arc; use core::marker::PhantomData; use core::mem; use core::ptr::{self, NonNull}; + use sync::Arc; /// A way of waking up a specific task. /// diff --git a/src/libstd/sync/mod.rs b/src/libstd/sync/mod.rs index 642b284c6c7..e12ef8d9eda 100644 --- a/src/libstd/sync/mod.rs +++ b/src/libstd/sync/mod.rs @@ -18,7 +18,7 @@ #![stable(feature = "rust1", since = "1.0.0")] #[stable(feature = "rust1", since = "1.0.0")] -pub use alloc_crate::arc::{Arc, Weak}; +pub use alloc_crate::sync::{Arc, Weak}; #[stable(feature = "rust1", since = "1.0.0")] pub use core::sync::atomic; -- cgit 1.4.1-3-g733a5 From 15bb6c431da6f486fd048a00ba9c72fe5bc2dd74 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 23 Jun 2018 21:48:56 +0200 Subject: liballoc docs: Remove “not intended for general usage” MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/liballoc/lib.rs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 8ec5a9ed193..c054042d5a1 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -13,10 +13,10 @@ //! This library provides smart pointers and collections for managing //! heap-allocated values. //! -//! This library, like libcore, is not intended for general usage, but rather as -//! a building block of other libraries. The types and interfaces in this -//! library are re-exported through the [standard library](../std/index.html), -//! and should not be used through this library. +//! This library, like libcore, normally doesn’t need to be used directly +//! since its contents are re-exported in the [`std` crate](../std/index.html). +//! Crates that use the `#![no_std]` attribute however will typically +//! not depend on `std`, so they’d use this crate instead. //! //! ## Boxed values //! -- cgit 1.4.1-3-g733a5 From ad97f8b491422d947c1c97d8e9f1bfecdb7f47ba Mon Sep 17 00:00:00 2001 From: Mark Simulacrum Date: Fri, 22 Jun 2018 09:48:43 -0600 Subject: Bootstrap from 1.28.0-beta.3 --- src/Cargo.lock | 29 +++--- src/bootstrap/channel.rs | 2 +- src/liballoc/lib.rs | 1 - src/libcore/intrinsics.rs | 4 - src/libcore/lib.rs | 1 - src/libcore/num/mod.rs | 192 -------------------------------------- src/libcore/num/wrapping.rs | 1 - src/libcore/panic.rs | 2 +- src/libcore/panicking.rs | 15 --- src/libcore/ptr.rs | 27 ------ src/libcore/slice/mod.rs | 3 - src/librustc/lib.rs | 1 - src/librustc_asan/lib.rs | 1 - src/librustc_lsan/lib.rs | 1 - src/librustc_metadata/lib.rs | 1 - src/librustc_msan/lib.rs | 1 - src/librustc_save_analysis/lib.rs | 1 - src/librustc_tsan/lib.rs | 1 - src/libstd/lib.rs | 5 +- src/libstd/panicking.rs | 100 +++++++------------- src/stage0.txt | 2 +- src/tools/cargo | 2 +- src/tools/rust-installer | 2 +- 23 files changed, 53 insertions(+), 342 deletions(-) (limited to 'src/liballoc') diff --git a/src/Cargo.lock b/src/Cargo.lock index a9339055264..5d09507c41b 100644 --- a/src/Cargo.lock +++ b/src/Cargo.lock @@ -191,13 +191,13 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "cargo" -version = "0.29.0" +version = "0.30.0" dependencies = [ "atty 0.2.8 (registry+https://github.com/rust-lang/crates.io-index)", "bufstream 0.1.3 (registry+https://github.com/rust-lang/crates.io-index)", "clap 2.31.2 (registry+https://github.com/rust-lang/crates.io-index)", "core-foundation 0.6.0 (registry+https://github.com/rust-lang/crates.io-index)", - "crates-io 0.17.0", + "crates-io 0.18.0", "crossbeam 0.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "crypto-hash 0.3.1 (registry+https://github.com/rust-lang/crates.io-index)", "curl 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", @@ -232,6 +232,7 @@ dependencies = [ "tempfile 3.0.2 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", "toml 0.4.6 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "url 1.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "winapi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -299,7 +300,7 @@ dependencies = [ "bitflags 1.0.1 (registry+https://github.com/rust-lang/crates.io-index)", "strsim 0.7.0 (registry+https://github.com/rust-lang/crates.io-index)", "textwrap 0.9.0 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", "vec_map 0.8.0 (registry+https://github.com/rust-lang/crates.io-index)", "yaml-rust 0.3.5 (registry+https://github.com/rust-lang/crates.io-index)", ] @@ -472,7 +473,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "crates-io" -version = "0.17.0" +version = "0.18.0" dependencies = [ "curl 0.4.12 (registry+https://github.com/rust-lang/crates.io-index)", "failure 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1683,7 +1684,7 @@ dependencies = [ name = "rls" version = "0.128.0" dependencies = [ - "cargo 0.29.0", + "cargo 0.30.0", "cargo_metadata 0.5.4 (registry+https://github.com/rust-lang/crates.io-index)", "clippy_lints 0.0.205 (registry+https://github.com/rust-lang/crates.io-index)", "env_logger 0.5.8 (registry+https://github.com/rust-lang/crates.io-index)", @@ -1882,7 +1883,7 @@ dependencies = [ "rustc-ap-serialize 149.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-syntax_pos 149.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1895,7 +1896,7 @@ dependencies = [ "rustc-ap-serialize 164.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-syntax_pos 164.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1969,7 +1970,7 @@ dependencies = [ "rustc-ap-rustc_data_structures 149.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-serialize 149.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -1981,7 +1982,7 @@ dependencies = [ "rustc-ap-rustc_data_structures 164.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "rustc-ap-serialize 164.0.0 (registry+https://github.com/rust-lang/crates.io-index)", "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2192,7 +2193,7 @@ dependencies = [ "serialize 0.0.0", "syntax_pos 0.0.0", "termcolor 0.3.6 (registry+https://github.com/rust-lang/crates.io-index)", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2783,7 +2784,7 @@ dependencies = [ "rustc_data_structures 0.0.0", "scoped-tls 0.1.1 (registry+https://github.com/rust-lang/crates.io-index)", "serialize 0.0.0", - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2908,7 +2909,7 @@ name = "textwrap" version = "0.9.0" source = "registry+https://github.com/rust-lang/crates.io-index" dependencies = [ - "unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)", + "unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)", ] [[package]] @@ -2984,7 +2985,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] name = "unicode-width" -version = "0.1.4" +version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" [[package]] @@ -3388,7 +3389,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" "checksum unicode-bidi 0.3.4 (registry+https://github.com/rust-lang/crates.io-index)" = "49f2bd0c6468a8230e1db229cff8029217cf623c767ea5d60bfbd42729ea54d5" "checksum unicode-normalization 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "51ccda9ef9efa3f7ef5d91e8f9b83bbe6955f9bf86aec89d5cce2c874625920f" "checksum unicode-segmentation 1.2.0 (registry+https://github.com/rust-lang/crates.io-index)" = "a8083c594e02b8ae1654ae26f0ade5158b119bd88ad0e8227a5d8fcd72407946" -"checksum unicode-width 0.1.4 (registry+https://github.com/rust-lang/crates.io-index)" = "bf3a113775714a22dcb774d8ea3655c53a32debae63a063acc00a91cc586245f" +"checksum unicode-width 0.1.5 (registry+https://github.com/rust-lang/crates.io-index)" = "882386231c45df4700b275c7ff55b6f3698780a650026380e72dabe76fa46526" "checksum unicode-xid 0.0.3 (registry+https://github.com/rust-lang/crates.io-index)" = "36dff09cafb4ec7c8cf0023eb0b686cb6ce65499116a12201c9e11840ca01beb" "checksum unicode-xid 0.0.4 (registry+https://github.com/rust-lang/crates.io-index)" = "8c1f860d7d29cf02cb2f3f359fd35991af3d30bac52c57d265a3c461074cb4dc" "checksum unicode-xid 0.1.0 (registry+https://github.com/rust-lang/crates.io-index)" = "fc72304796d0818e357ead4e000d19c9c174ab23dc11093ac919054d20a6a7fc" diff --git a/src/bootstrap/channel.rs b/src/bootstrap/channel.rs index a2495f68c1f..04d576df955 100644 --- a/src/bootstrap/channel.rs +++ b/src/bootstrap/channel.rs @@ -24,7 +24,7 @@ use Build; use config::Config; // The version number -pub const CFG_RELEASE_NUM: &str = "1.28.0"; +pub const CFG_RELEASE_NUM: &str = "1.29.0"; pub struct GitInfo { inner: Option, diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ec9b5eba561..42fc716bae2 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -105,7 +105,6 @@ #![feature(pin)] #![feature(ptr_internals)] #![feature(ptr_offset_from)] -#![cfg_attr(stage0, feature(repr_transparent))] #![feature(rustc_attrs)] #![feature(specialization)] #![feature(split_ascii_whitespace)] diff --git a/src/libcore/intrinsics.rs b/src/libcore/intrinsics.rs index 95f13daf841..89fe2d941a3 100644 --- a/src/libcore/intrinsics.rs +++ b/src/libcore/intrinsics.rs @@ -1364,10 +1364,6 @@ extern "rust-intrinsic" { /// source as well as std's catch implementation. pub fn try(f: fn(*mut u8), data: *mut u8, local_ptr: *mut u8) -> i32; - #[cfg(stage0)] - /// docs my friends, its friday! - pub fn align_offset(ptr: *const (), align: usize) -> usize; - /// Emits a `!nontemporal` store according to LLVM (see their docs). /// Probably will never become stable. pub fn nontemporal_store(ptr: *mut T, val: T); diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index 40caee85541..cef126f36e8 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -100,7 +100,6 @@ #![feature(optin_builtin_traits)] #![feature(prelude_import)] #![feature(repr_simd, platform_intrinsics)] -#![cfg_attr(stage0, feature(repr_transparent))] #![feature(rustc_attrs)] #![feature(rustc_const_unstable)] #![feature(simd_ffi)] diff --git a/src/libcore/num/mod.rs b/src/libcore/num/mod.rs index 7e2dd304d7f..e0d267a6ce0 100644 --- a/src/libcore/num/mod.rs +++ b/src/libcore/num/mod.rs @@ -267,20 +267,11 @@ $EndFeature, " ``` "), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn count_ones(self) -> u32 { (self as $UnsignedT).count_ones() } - } - doc_comment! { concat!("Returns the number of zeros in the binary representation of `self`. @@ -292,7 +283,6 @@ Basic usage: ", $Feature, "assert_eq!(", stringify!($SelfT), "::max_value().count_zeros(), 1);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn count_zeros(self) -> u32 { @@ -300,16 +290,6 @@ Basic usage: } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentatio"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn count_zeros(self) -> u32 { - (!self).count_ones() - } - } - doc_comment! { concat!("Returns the number of leading zeros in the binary representation of `self`. @@ -324,7 +304,6 @@ assert_eq!(n.leading_zeros(), 0);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn leading_zeros(self) -> u32 { @@ -332,16 +311,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn leading_zeros(self) -> u32 { - (self as $UnsignedT).leading_zeros() - } - } - doc_comment! { concat!("Returns the number of trailing zeros in the binary representation of `self`. @@ -356,7 +325,6 @@ assert_eq!(n.trailing_zeros(), 2);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn trailing_zeros(self) -> u32 { @@ -364,16 +332,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn trailing_zeros(self) -> u32 { - (self as $UnsignedT).trailing_zeros() - } - } - /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -442,21 +400,12 @@ $EndFeature, " /// assert_eq!(m, 21760); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn swap_bytes(self) -> Self { (self as $UnsignedT).swap_bytes() as Self } - /// Dummy docs. See !stage0 documentation. - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn swap_bytes(self) -> Self { - (self as $UnsignedT).swap_bytes() as Self - } - /// Reverses the bit pattern of the integer. /// /// # Examples @@ -503,7 +452,6 @@ if cfg!(target_endian = \"big\") { $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn from_be(x: Self) -> Self { @@ -518,16 +466,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn from_be(x: Self) -> Self { - if cfg!(target_endian = "big") { x } else { x.swap_bytes() } - } - } - doc_comment! { concat!("Converts an integer from little endian to the target's endianness. @@ -548,7 +486,6 @@ if cfg!(target_endian = \"little\") { $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn from_le(x: Self) -> Self { @@ -563,16 +500,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn from_le(x: Self) -> Self { - if cfg!(target_endian = "little") { x } else { x.swap_bytes() } - } - } - doc_comment! { concat!("Converts `self` to big endian from the target's endianness. @@ -593,7 +520,6 @@ if cfg!(target_endian = \"big\") { $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn to_be(self) -> Self { // or not to be? @@ -608,16 +534,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn to_be(self) -> Self { // or not to be? - if cfg!(target_endian = "big") { self } else { self.swap_bytes() } - } - } - doc_comment! { concat!("Converts `self` to little endian from the target's endianness. @@ -638,7 +554,6 @@ if cfg!(target_endian = \"little\") { $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn to_le(self) -> Self { @@ -653,16 +568,6 @@ $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn to_le(self) -> Self { - if cfg!(target_endian = "little") { self } else { self.swap_bytes() } - } - } - doc_comment! { concat!("Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred. @@ -2161,7 +2066,6 @@ Basic usage: assert_eq!(n.count_ones(), 3);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn count_ones(self) -> u32 { @@ -2169,16 +2073,6 @@ assert_eq!(n.count_ones(), 3);", $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn count_ones(self) -> u32 { - unsafe { intrinsics::ctpop(self as $ActualT) as u32 } - } - } - doc_comment! { concat!("Returns the number of zeros in the binary representation of `self`. @@ -2190,7 +2084,6 @@ Basic usage: ", $Feature, "assert_eq!(", stringify!($SelfT), "::max_value().count_zeros(), 0);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn count_zeros(self) -> u32 { @@ -2198,16 +2091,6 @@ Basic usage: } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn count_zeros(self) -> u32 { - (!self).count_ones() - } - } - doc_comment! { concat!("Returns the number of leading zeros in the binary representation of `self`. @@ -2221,7 +2104,6 @@ Basic usage: assert_eq!(n.leading_zeros(), 2);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn leading_zeros(self) -> u32 { @@ -2229,16 +2111,6 @@ assert_eq!(n.leading_zeros(), 2);", $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn leading_zeros(self) -> u32 { - unsafe { intrinsics::ctlz(self as $ActualT) as u32 } - } - } - doc_comment! { concat!("Returns the number of trailing zeros in the binary representation of `self`. @@ -2253,7 +2125,6 @@ Basic usage: assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn trailing_zeros(self) -> u32 { @@ -2261,16 +2132,6 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn trailing_zeros(self) -> u32 { - unsafe { uint_cttz_call!(self, $BITS) as u32 } - } - } - /// Shifts the bits to the left by a specified amount, `n`, /// wrapping the truncated bits to the end of the resulting integer. /// @@ -2343,21 +2204,12 @@ assert_eq!(n.trailing_zeros(), 3);", $EndFeature, " /// assert_eq!(m, 21760); /// ``` #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn swap_bytes(self) -> Self { unsafe { intrinsics::bswap(self as $ActualT) as Self } } - /// Dummy docs. See !stage0 documentation. - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn swap_bytes(self) -> Self { - unsafe { intrinsics::bswap(self as $ActualT) as Self } - } - /// Reverses the bit pattern of the integer. /// /// # Examples @@ -2404,7 +2256,6 @@ if cfg!(target_endian = \"big\") { }", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn from_be(x: Self) -> Self { @@ -2419,16 +2270,6 @@ if cfg!(target_endian = \"big\") { } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn from_be(x: Self) -> Self { - if cfg!(target_endian = "big") { x } else { x.swap_bytes() } - } - } - doc_comment! { concat!("Converts an integer from little endian to the target's endianness. @@ -2449,7 +2290,6 @@ if cfg!(target_endian = \"little\") { }", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn from_le(x: Self) -> Self { @@ -2464,16 +2304,6 @@ if cfg!(target_endian = \"little\") { } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn from_le(x: Self) -> Self { - if cfg!(target_endian = "little") { x } else { x.swap_bytes() } - } - } - doc_comment! { concat!("Converts `self` to big endian from the target's endianness. @@ -2494,7 +2324,6 @@ if cfg!(target_endian = \"big\") { }", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn to_be(self) -> Self { // or not to be? @@ -2509,16 +2338,6 @@ if cfg!(target_endian = \"big\") { } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn to_be(self) -> Self { // or not to be? - if cfg!(target_endian = "big") { self } else { self.swap_bytes() } - } - } - doc_comment! { concat!("Converts `self` to little endian from the target's endianness. @@ -2539,7 +2358,6 @@ if cfg!(target_endian = \"little\") { }", $EndFeature, " ```"), #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(not(stage0))] #[rustc_const_unstable(feature = "const_int_ops")] #[inline] pub const fn to_le(self) -> Self { @@ -2554,16 +2372,6 @@ if cfg!(target_endian = \"little\") { } } - doc_comment! { - concat!("Dummy docs. See !stage0 documentation"), - #[stable(feature = "rust1", since = "1.0.0")] - #[cfg(stage0)] - #[inline] - pub fn to_le(self) -> Self { - if cfg!(target_endian = "little") { self } else { self.swap_bytes() } - } - } - doc_comment! { concat!("Checked integer addition. Computes `self + rhs`, returning `None` if overflow occurred. diff --git a/src/libcore/num/wrapping.rs b/src/libcore/num/wrapping.rs index d7f87d37f5b..1c826c2fa76 100644 --- a/src/libcore/num/wrapping.rs +++ b/src/libcore/num/wrapping.rs @@ -531,7 +531,6 @@ assert_eq!(n.trailing_zeros(), 3); /// assert_eq!(m, Wrapping(-22016)); /// ``` #[unstable(feature = "reverse_bits", issue = "48763")] - #[cfg(not(stage0))] #[inline] pub fn reverse_bits(self) -> Self { Wrapping(self.0.reverse_bits()) diff --git a/src/libcore/panic.rs b/src/libcore/panic.rs index 37ae05309af..1b4129b99fc 100644 --- a/src/libcore/panic.rs +++ b/src/libcore/panic.rs @@ -35,7 +35,7 @@ use fmt; /// /// panic!("Normal panic"); /// ``` -#[cfg_attr(not(stage0), lang = "panic_info")] +#[lang = "panic_info"] #[stable(feature = "panic_hooks", since = "1.10.0")] #[derive(Debug)] pub struct PanicInfo<'a> { diff --git a/src/libcore/panicking.rs b/src/libcore/panicking.rs index 0d4f8d1141e..58407de9566 100644 --- a/src/libcore/panicking.rs +++ b/src/libcore/panicking.rs @@ -37,7 +37,6 @@ issue = "0")] use fmt; -#[cfg(not(stage0))] use panic::{Location, PanicInfo}; #[cold] #[inline(never)] // this is the slow path, always @@ -61,20 +60,6 @@ fn panic_bounds_check(file_line_col: &(&'static str, u32, u32), len, index), file_line_col) } -#[cfg(stage0)] -#[cold] #[inline(never)] -pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { - #[allow(improper_ctypes)] - extern { - #[lang = "panic_fmt"] - #[unwind(allowed)] - fn panic_impl(fmt: fmt::Arguments, file: &'static str, line: u32, col: u32) -> !; - } - let (file, line, col) = *file_line_col; - unsafe { panic_impl(fmt, file, line, col) } -} - -#[cfg(not(stage0))] #[cold] #[inline(never)] pub fn panic_fmt(fmt: fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { // NOTE This function never crosses the FFI boundary; it's a Rust-to-Rust call diff --git a/src/libcore/ptr.rs b/src/libcore/ptr.rs index 81a8b3ef047..164b29d3515 100644 --- a/src/libcore/ptr.rs +++ b/src/libcore/ptr.rs @@ -1243,7 +1243,6 @@ impl *const T { /// # } } /// ``` #[unstable(feature = "align_offset", issue = "44488")] - #[cfg(not(stage0))] pub fn align_offset(self, align: usize) -> usize where T: Sized { if !align.is_power_of_two() { panic!("align_offset: align is not a power-of-two"); @@ -1252,18 +1251,6 @@ impl *const T { align_offset(self, align) } } - - /// definitely docs. - #[unstable(feature = "align_offset", issue = "44488")] - #[cfg(stage0)] - pub fn align_offset(self, align: usize) -> usize where T: Sized { - if !align.is_power_of_two() { - panic!("align_offset: align is not a power-of-two"); - } - unsafe { - intrinsics::align_offset(self as *const (), align) - } - } } @@ -2308,7 +2295,6 @@ impl *mut T { /// # } } /// ``` #[unstable(feature = "align_offset", issue = "44488")] - #[cfg(not(stage0))] pub fn align_offset(self, align: usize) -> usize where T: Sized { if !align.is_power_of_two() { panic!("align_offset: align is not a power-of-two"); @@ -2317,18 +2303,6 @@ impl *mut T { align_offset(self, align) } } - - /// definitely docs. - #[unstable(feature = "align_offset", issue = "44488")] - #[cfg(stage0)] - pub fn align_offset(self, align: usize) -> usize where T: Sized { - if !align.is_power_of_two() { - panic!("align_offset: align is not a power-of-two"); - } - unsafe { - intrinsics::align_offset(self as *const (), align) - } - } } /// Align pointer `p`. @@ -2346,7 +2320,6 @@ impl *mut T { /// /// Any questions go to @nagisa. #[lang="align_offset"] -#[cfg(not(stage0))] pub(crate) unsafe fn align_offset(p: *const T, a: usize) -> usize { /// Calculate multiplicative modular inverse of `x` modulo `m`. /// diff --git a/src/libcore/slice/mod.rs b/src/libcore/slice/mod.rs index e74e527927d..bcac9322ae8 100644 --- a/src/libcore/slice/mod.rs +++ b/src/libcore/slice/mod.rs @@ -1708,7 +1708,6 @@ impl [T] { } /// Function to calculate lenghts of the middle and trailing slice for `align_to{,_mut}`. - #[cfg(not(stage0))] fn align_to_offsets(&self) -> (usize, usize) { // What we gonna do about `rest` is figure out what multiple of `U`s we can put in a // lowest number of `T`s. And how many `T`s we need for each such "multiple". @@ -1798,7 +1797,6 @@ impl [T] { /// } /// ``` #[unstable(feature = "slice_align_to", issue = "44488")] - #[cfg(not(stage0))] pub unsafe fn align_to(&self) -> (&[T], &[U], &[T]) { // Note that most of this function will be constant-evaluated, if ::mem::size_of::() == 0 || ::mem::size_of::() == 0 { @@ -1851,7 +1849,6 @@ impl [T] { /// } /// ``` #[unstable(feature = "slice_align_to", issue = "44488")] - #[cfg(not(stage0))] pub unsafe fn align_to_mut(&mut self) -> (&mut [T], &mut [U], &mut [T]) { // Note that most of this function will be constant-evaluated, if ::mem::size_of::() == 0 || ::mem::size_of::() == 0 { diff --git a/src/librustc/lib.rs b/src/librustc/lib.rs index 102efe2bef3..95e2d0e2edb 100644 --- a/src/librustc/lib.rs +++ b/src/librustc/lib.rs @@ -49,7 +49,6 @@ #![feature(fs_read_write)] #![feature(iterator_find_map)] #![cfg_attr(windows, feature(libc))] -#![cfg_attr(stage0, feature(macro_lifetime_matcher))] #![feature(macro_vis_matcher)] #![feature(never_type)] #![feature(exhaustive_patterns)] diff --git a/src/librustc_asan/lib.rs b/src/librustc_asan/lib.rs index a7aeed76309..0c78fd74a23 100644 --- a/src/librustc_asan/lib.rs +++ b/src/librustc_asan/lib.rs @@ -10,7 +10,6 @@ #![sanitizer_runtime] #![feature(alloc_system)] -#![cfg_attr(stage0, feature(global_allocator))] #![feature(sanitizer_runtime)] #![feature(staged_api)] #![no_std] diff --git a/src/librustc_lsan/lib.rs b/src/librustc_lsan/lib.rs index a7aeed76309..0c78fd74a23 100644 --- a/src/librustc_lsan/lib.rs +++ b/src/librustc_lsan/lib.rs @@ -10,7 +10,6 @@ #![sanitizer_runtime] #![feature(alloc_system)] -#![cfg_attr(stage0, feature(global_allocator))] #![feature(sanitizer_runtime)] #![feature(staged_api)] #![no_std] diff --git a/src/librustc_metadata/lib.rs b/src/librustc_metadata/lib.rs index 0fbedcaff6e..dad1030cb61 100644 --- a/src/librustc_metadata/lib.rs +++ b/src/librustc_metadata/lib.rs @@ -16,7 +16,6 @@ #![feature(fs_read_write)] #![feature(libc)] #![feature(macro_at_most_once_rep)] -#![cfg_attr(stage0, feature(macro_lifetime_matcher))] #![feature(proc_macro_internals)] #![feature(quote)] #![feature(rustc_diagnostic_macros)] diff --git a/src/librustc_msan/lib.rs b/src/librustc_msan/lib.rs index a7aeed76309..0c78fd74a23 100644 --- a/src/librustc_msan/lib.rs +++ b/src/librustc_msan/lib.rs @@ -10,7 +10,6 @@ #![sanitizer_runtime] #![feature(alloc_system)] -#![cfg_attr(stage0, feature(global_allocator))] #![feature(sanitizer_runtime)] #![feature(staged_api)] #![no_std] diff --git a/src/librustc_save_analysis/lib.rs b/src/librustc_save_analysis/lib.rs index 89d30fd666a..4c9cdc1e6d3 100644 --- a/src/librustc_save_analysis/lib.rs +++ b/src/librustc_save_analysis/lib.rs @@ -12,7 +12,6 @@ html_favicon_url = "https://doc.rust-lang.org/favicon.ico", html_root_url = "https://doc.rust-lang.org/nightly/")] #![feature(custom_attribute)] -#![cfg_attr(stage0, feature(macro_lifetime_matcher))] #![allow(unused_attributes)] #![recursion_limit="256"] diff --git a/src/librustc_tsan/lib.rs b/src/librustc_tsan/lib.rs index a7aeed76309..0c78fd74a23 100644 --- a/src/librustc_tsan/lib.rs +++ b/src/librustc_tsan/lib.rs @@ -10,7 +10,6 @@ #![sanitizer_runtime] #![feature(alloc_system)] -#![cfg_attr(stage0, feature(global_allocator))] #![feature(sanitizer_runtime)] #![feature(staged_api)] #![no_std] diff --git a/src/libstd/lib.rs b/src/libstd/lib.rs index caad924ea5b..d73cb1f8349 100644 --- a/src/libstd/lib.rs +++ b/src/libstd/lib.rs @@ -322,7 +322,7 @@ #![feature(doc_keyword)] #![feature(float_internals)] #![feature(panic_info_message)] -#![cfg_attr(not(stage0), feature(panic_implementation))] +#![feature(panic_implementation)] #![default_lib_allocator] @@ -332,9 +332,6 @@ // `force_alloc_system` is *only* intended as a workaround for local rebuilds // with a rustc without jemalloc. // FIXME(#44236) shouldn't need MSVC logic -#![cfg_attr(all(not(target_env = "msvc"), - any(all(stage0, not(test)), feature = "force_alloc_system")), - feature(global_allocator))] #[cfg(all(not(target_env = "msvc"), any(all(stage0, not(test)), feature = "force_alloc_system")))] #[global_allocator] diff --git a/src/libstd/panicking.rs b/src/libstd/panicking.rs index 0808efa2ece..46b6cf60705 100644 --- a/src/libstd/panicking.rs +++ b/src/libstd/panicking.rs @@ -319,18 +319,6 @@ pub fn panicking() -> bool { /// Entry point of panic from the libcore crate. #[cfg(not(test))] -#[cfg(stage0)] -#[lang = "panic_fmt"] -pub extern fn rust_begin_panic(msg: fmt::Arguments, - file: &'static str, - line: u32, - col: u32) -> ! { - begin_panic_fmt(&msg, &(file, line, col)) -} - -/// Entry point of panic from the libcore crate. -#[cfg(not(test))] -#[cfg(not(stage0))] #[panic_implementation] #[unwind(allowed)] pub fn rust_begin_panic(info: &PanicInfo) -> ! { @@ -343,78 +331,54 @@ pub fn rust_begin_panic(info: &PanicInfo) -> ! { /// site as much as possible (so that `panic!()` has as low an impact /// on (e.g.) the inlining of other functions as possible), by moving /// the actual formatting into this shared place. -#[cfg(stage0)] #[unstable(feature = "libstd_sys_internals", reason = "used by the panic! macro", issue = "0")] #[inline(never)] #[cold] pub fn begin_panic_fmt(msg: &fmt::Arguments, file_line_col: &(&'static str, u32, u32)) -> ! { - // We do two allocations here, unfortunately. But (a) they're - // required with the current scheme, and (b) we don't handle - // panic + OOM properly anyway (see comment in begin_panic - // below). - - rust_panic_with_hook(&mut PanicPayload::new(msg), Some(msg), file_line_col); -} - -// NOTE(stage0) move into `continue_panic_fmt` on next stage0 update -struct PanicPayload<'a> { - inner: &'a fmt::Arguments<'a>, - string: Option, + let (file, line, col) = *file_line_col; + let info = PanicInfo::internal_constructor( + Some(msg), + Location::internal_constructor(file, line, col), + ); + continue_panic_fmt(&info) } -impl<'a> PanicPayload<'a> { - fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> { - PanicPayload { inner, string: None } +fn continue_panic_fmt(info: &PanicInfo) -> ! { + struct PanicPayload<'a> { + inner: &'a fmt::Arguments<'a>, + string: Option, } - fn fill(&mut self) -> &mut String { - use fmt::Write; + impl<'a> PanicPayload<'a> { + fn new(inner: &'a fmt::Arguments<'a>) -> PanicPayload<'a> { + PanicPayload { inner, string: None } + } - let inner = self.inner; - self.string.get_or_insert_with(|| { - let mut s = String::new(); - drop(s.write_fmt(*inner)); - s - }) - } -} + fn fill(&mut self) -> &mut String { + use fmt::Write; -unsafe impl<'a> BoxMeUp for PanicPayload<'a> { - fn box_me_up(&mut self) -> *mut (Any + Send) { - let contents = mem::replace(self.fill(), String::new()); - Box::into_raw(Box::new(contents)) + let inner = self.inner; + self.string.get_or_insert_with(|| { + let mut s = String::new(); + drop(s.write_fmt(*inner)); + s + }) + } } - fn get(&mut self) -> &(Any + Send) { - self.fill() - } -} + unsafe impl<'a> BoxMeUp for PanicPayload<'a> { + fn box_me_up(&mut self) -> *mut (Any + Send) { + let contents = mem::replace(self.fill(), String::new()); + Box::into_raw(Box::new(contents)) + } -/// The entry point for panicking with a formatted message. -/// -/// This is designed to reduce the amount of code required at the call -/// site as much as possible (so that `panic!()` has as low an impact -/// on (e.g.) the inlining of other functions as possible), by moving -/// the actual formatting into this shared place. -#[cfg(not(stage0))] -#[unstable(feature = "libstd_sys_internals", - reason = "used by the panic! macro", - issue = "0")] -#[inline(never)] #[cold] -pub fn begin_panic_fmt(msg: &fmt::Arguments, - file_line_col: &(&'static str, u32, u32)) -> ! { - let (file, line, col) = *file_line_col; - let info = PanicInfo::internal_constructor( - Some(msg), - Location::internal_constructor(file, line, col), - ); - continue_panic_fmt(&info) -} + fn get(&mut self) -> &(Any + Send) { + self.fill() + } + } -#[cfg(not(stage0))] -fn continue_panic_fmt(info: &PanicInfo) -> ! { // We do two allocations here, unfortunately. But (a) they're // required with the current scheme, and (b) we don't handle // panic + OOM properly anyway (see comment in begin_panic diff --git a/src/stage0.txt b/src/stage0.txt index 435cfd2f6db..538f3a85176 100644 --- a/src/stage0.txt +++ b/src/stage0.txt @@ -12,7 +12,7 @@ # source tarball for a stable release you'll likely see `1.x.0` for rustc and # `0.x.0` for Cargo where they were released on `date`. -date: 2018-05-10 +date: 2018-06-30 rustc: beta cargo: beta diff --git a/src/tools/cargo b/src/tools/cargo index e2348c2db29..5699afe508d 160000 --- a/src/tools/cargo +++ b/src/tools/cargo @@ -1 +1 @@ -Subproject commit e2348c2db296ce33428933c3ab8786d5f3c54a2e +Subproject commit 5699afe508d62924f6b38b19dc98296ad33d1659 diff --git a/src/tools/rust-installer b/src/tools/rust-installer index 118e078c5ba..89414e44dc9 160000 --- a/src/tools/rust-installer +++ b/src/tools/rust-installer @@ -1 +1 @@ -Subproject commit 118e078c5badd520d18b92813fd88789c8d341ab +Subproject commit 89414e44dc94844888e59c08bc31dcccb1792800 -- cgit 1.4.1-3-g733a5 From f46f05bae8d81246f1ad93ccee051a13c9be393e Mon Sep 17 00:00:00 2001 From: Nicholas Nethercote Date: Fri, 29 Jun 2018 17:02:38 +1000 Subject: Make `BTreeMap::clone()` not allocate when cloning an empty tree. --- src/liballoc/collections/btree/map.rs | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index e6e454446e2..2aad3149bb2 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -213,7 +213,16 @@ impl Clone for BTreeMap { } } - clone_subtree(self.root.as_ref()) + if self.len() == 0 { + // Ideally we'd call `BTreeMap::new` here, but that has the `K: + // Ord` constraint, which this method lacks. + BTreeMap { + root: node::Root::shared_empty_root(), + length: 0, + } + } else { + clone_subtree(self.root.as_ref()) + } } } -- cgit 1.4.1-3-g733a5 From 3d43f828f5f1261b88f00582074f6cd021f31614 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Sat, 30 Jun 2018 17:26:38 +0200 Subject: Make custom trait object for `Future` generic --- src/liballoc/boxed.rs | 30 ++++---- src/libcore/task/executor.rs | 8 +-- src/libcore/task/future_obj.rs | 147 +++++++++++++++++++++++++++++++++++++++ src/libcore/task/mod.rs | 4 +- src/libcore/task/task.rs | 142 ------------------------------------- src/test/run-pass/async-await.rs | 4 +- src/test/run-pass/futures-api.rs | 4 +- 7 files changed, 172 insertions(+), 167 deletions(-) create mode 100644 src/libcore/task/future_obj.rs delete mode 100644 src/libcore/task/task.rs (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6a05ef68088..c1778ef101a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -66,7 +66,7 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll, UnsafeTask, TaskObj, LocalTaskObj}; +use core::task::{Context, Poll, UnsafeFutureObj, FutureObj, LocalFutureObj}; use core::convert::From; use raw_vec::RawVec; @@ -933,12 +933,12 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + 'static> UnsafeTask for PinBox { +unsafe impl + 'static> UnsafeFutureObj for PinBox { fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } - unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()> { + unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll { let ptr = task as *mut F; let pin: PinMut = PinMut::new_unchecked(&mut *ptr); pin.poll(cx) @@ -950,29 +950,29 @@ unsafe impl + 'static> UnsafeTask for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> From> for TaskObj { - fn from(boxed: PinBox) -> Self { - TaskObj::new(boxed) +impl + Send + 'static> Into> for PinBox { + fn into(self) -> FutureObj { + FutureObj::new(self) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> From> for TaskObj { - fn from(boxed: Box) -> Self { - TaskObj::new(PinBox::from(boxed)) +impl + Send + 'static> Into> for Box { + fn into(self) -> FutureObj { + FutureObj::new(PinBox::from(self)) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> From> for LocalTaskObj { - fn from(boxed: PinBox) -> Self { - LocalTaskObj::new(boxed) +impl + 'static> Into> for PinBox { + fn into(self) -> LocalFutureObj { + LocalFutureObj::new(self) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> From> for LocalTaskObj { - fn from(boxed: Box) -> Self { - LocalTaskObj::new(PinBox::from(boxed)) +impl + 'static> Into> for Box { + fn into(self) -> LocalFutureObj { + LocalFutureObj::new(PinBox::from(self)) } } diff --git a/src/libcore/task/executor.rs b/src/libcore/task/executor.rs index 73bf80d2f99..55ea5e724c1 100644 --- a/src/libcore/task/executor.rs +++ b/src/libcore/task/executor.rs @@ -13,7 +13,7 @@ issue = "50547")] use fmt; -use super::{TaskObj, LocalTaskObj}; +use super::{FutureObj, LocalFutureObj}; /// A task executor. /// @@ -29,7 +29,7 @@ pub trait Executor { /// /// The executor may be unable to spawn tasks, either because it has /// been shut down or is resource-constrained. - fn spawn_obj(&mut self, task: TaskObj) -> Result<(), SpawnObjError>; + fn spawn_obj(&mut self, task: FutureObj<()>) -> Result<(), SpawnObjError>; /// Determine whether the executor is able to spawn new tasks. /// @@ -76,7 +76,7 @@ pub struct SpawnObjError { pub kind: SpawnErrorKind, /// The task for which spawning was attempted - pub task: TaskObj, + pub task: FutureObj<()>, } /// The result of a failed spawn @@ -86,5 +86,5 @@ pub struct SpawnLocalObjError { pub kind: SpawnErrorKind, /// The task for which spawning was attempted - pub task: LocalTaskObj, + pub task: LocalFutureObj<()>, } diff --git a/src/libcore/task/future_obj.rs b/src/libcore/task/future_obj.rs new file mode 100644 index 00000000000..3ed3bd51cf6 --- /dev/null +++ b/src/libcore/task/future_obj.rs @@ -0,0 +1,147 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use fmt; +use future::Future; +use marker::PhantomData; +use mem::PinMut; +use task::{Context, Poll}; + +/// A custom trait object for polling futures, roughly akin to +/// `Box>`. +/// Contrary to `FutureObj`, `LocalFutureObj` does not have a `Send` bound. +pub struct LocalFutureObj { + ptr: *mut (), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll, + drop_fn: unsafe fn(*mut ()), + _marker: PhantomData, +} + +impl LocalFutureObj { + /// Create a `LocalFutureObj` from a custom trait object representation. + #[inline] + pub fn new>(f: F) -> LocalFutureObj { + LocalFutureObj { + ptr: f.into_raw(), + poll_fn: F::poll, + drop_fn: F::drop, + _marker: PhantomData, + } + } + + /// Converts the `LocalFutureObj` into a `FutureObj` + /// To make this operation safe one has to ensure that the `UnsafeFutureObj` + /// instance from which this `LocalFutureObj` was created actually + /// implements `Send`. + #[inline] + pub unsafe fn as_future_obj(self) -> FutureObj { + FutureObj(self) + } +} + +impl fmt::Debug for LocalFutureObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LocalFutureObj") + .finish() + } +} + +impl From> for LocalFutureObj { + #[inline] + fn from(f: FutureObj) -> LocalFutureObj { + f.0 + } +} + +impl Future for LocalFutureObj { + type Output = T; + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll { + unsafe { + (self.poll_fn)(self.ptr, cx) + } + } +} + +impl Drop for LocalFutureObj { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.ptr) + } + } +} + +/// A custom trait object for polling futures, roughly akin to +/// `Box> + Send`. +pub struct FutureObj(LocalFutureObj); + +unsafe impl Send for FutureObj {} + +impl FutureObj { + /// Create a `FutureObj` from a custom trait object representation. + #[inline] + pub fn new + Send>(f: F) -> FutureObj { + FutureObj(LocalFutureObj::new(f)) + } +} + +impl fmt::Debug for FutureObj { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FutureObj") + .finish() + } +} + +impl Future for FutureObj { + type Output = T; + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll { + let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) }; + pinned_field.poll(cx) + } +} + +/// A custom implementation of a future trait object for `FutureObj`, providing +/// a hand-rolled vtable. +/// +/// This custom representation is typically used only in `no_std` contexts, +/// where the default `Box`-based implementation is not available. +/// +/// The implementor must guarantee that it is safe to call `poll` repeatedly (in +/// a non-concurrent fashion) with the result of `into_raw` until `drop` is +/// called. +pub unsafe trait UnsafeFutureObj: 'static { + /// Convert a owned instance into a (conceptually owned) void pointer. + fn into_raw(self) -> *mut (); + + /// Poll the future represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to repeatedly call + /// `poll` with the result of `into_raw` until `drop` is called; such calls + /// are not, however, allowed to race with each other or with calls to `drop`. + unsafe fn poll(future: *mut (), cx: &mut Context) -> Poll; + + /// Drops the future represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to call this + /// function once per `into_raw` invocation; that call cannot race with + /// other calls to `drop` or `poll`. + unsafe fn drop(future: *mut ()); +} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index d167a374105..06cd7a9dd77 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -25,8 +25,8 @@ pub use self::executor::{ mod poll; pub use self::poll::Poll; -mod task; -pub use self::task::{TaskObj, LocalTaskObj, UnsafeTask}; +mod future_obj; +pub use self::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj}; mod wake; pub use self::wake::{Waker, LocalWaker, UnsafeWake}; diff --git a/src/libcore/task/task.rs b/src/libcore/task/task.rs deleted file mode 100644 index c5a41873db4..00000000000 --- a/src/libcore/task/task.rs +++ /dev/null @@ -1,142 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] - -use fmt; -use future::Future; -use mem::PinMut; -use super::{Context, Poll}; - -/// A custom trait object for polling tasks, roughly akin to -/// `Box>`. -/// Contrary to `TaskObj`, `LocalTaskObj` does not have a `Send` bound. -pub struct LocalTaskObj { - ptr: *mut (), - poll_fn: unsafe fn(*mut (), &mut Context) -> Poll<()>, - drop_fn: unsafe fn(*mut ()), -} - -impl LocalTaskObj { - /// Create a `LocalTaskObj` from a custom trait object representation. - #[inline] - pub fn new(t: T) -> LocalTaskObj { - LocalTaskObj { - ptr: t.into_raw(), - poll_fn: T::poll, - drop_fn: T::drop, - } - } - - /// Converts the `LocalTaskObj` into a `TaskObj` - /// To make this operation safe one has to ensure that the `UnsafeTask` - /// instance from which this `LocalTaskObj` was created actually implements - /// `Send`. - pub unsafe fn as_task_obj(self) -> TaskObj { - TaskObj(self) - } -} - -impl fmt::Debug for LocalTaskObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LocalTaskObj") - .finish() - } -} - -impl From for LocalTaskObj { - fn from(task: TaskObj) -> LocalTaskObj { - task.0 - } -} - -impl Future for LocalTaskObj { - type Output = (); - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { - unsafe { - (self.poll_fn)(self.ptr, cx) - } - } -} - -impl Drop for LocalTaskObj { - fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.ptr) - } - } -} - -/// A custom trait object for polling tasks, roughly akin to -/// `Box + Send>`. -pub struct TaskObj(LocalTaskObj); - -unsafe impl Send for TaskObj {} - -impl TaskObj { - /// Create a `TaskObj` from a custom trait object representation. - #[inline] - pub fn new(t: T) -> TaskObj { - TaskObj(LocalTaskObj::new(t)) - } -} - -impl fmt::Debug for TaskObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("TaskObj") - .finish() - } -} - -impl Future for TaskObj { - type Output = (); - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll<()> { - let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) }; - pinned_field.poll(cx) - } -} - -/// A custom implementation of a task trait object for `TaskObj`, providing -/// a hand-rolled vtable. -/// -/// This custom representation is typically used only in `no_std` contexts, -/// where the default `Box`-based implementation is not available. -/// -/// The implementor must guarantee that it is safe to call `poll` repeatedly (in -/// a non-concurrent fashion) with the result of `into_raw` until `drop` is -/// called. -pub unsafe trait UnsafeTask: 'static { - /// Convert a owned instance into a (conceptually owned) void pointer. - fn into_raw(self) -> *mut (); - - /// Poll the task represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to repeatedly call - /// `poll` with the result of `into_raw` until `drop` is called; such calls - /// are not, however, allowed to race with each other or with calls to `drop`. - unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll<()>; - - /// Drops the task represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to call this - /// function once per `into_raw` invocation; that call cannot race with - /// other calls to `drop` or `poll`. - unsafe fn drop(task: *mut ()); -} diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index 8b649f6ef7b..3a67750e77e 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -21,7 +21,7 @@ use std::sync::{ }; use std::task::{ Context, Poll, Wake, - Executor, TaskObj, SpawnObjError, + Executor, FutureObj, SpawnObjError, local_waker_from_nonlocal, }; @@ -37,7 +37,7 @@ impl Wake for Counter { struct NoopExecutor; impl Executor for NoopExecutor { - fn spawn_obj(&mut self, _: TaskObj) -> Result<(), SpawnObjError> { + fn spawn_obj(&mut self, _: FutureObj) -> Result<(), SpawnObjError> { Ok(()) } } diff --git a/src/test/run-pass/futures-api.rs b/src/test/run-pass/futures-api.rs index 3b5a1725b66..a427b82af6a 100644 --- a/src/test/run-pass/futures-api.rs +++ b/src/test/run-pass/futures-api.rs @@ -22,7 +22,7 @@ use std::sync::{ use std::task::{ Context, Poll, Wake, Waker, LocalWaker, - Executor, TaskObj, SpawnObjError, + Executor, FutureObj, SpawnObjError, local_waker, local_waker_from_nonlocal, }; @@ -44,7 +44,7 @@ impl Wake for Counter { struct NoopExecutor; impl Executor for NoopExecutor { - fn spawn_obj(&mut self, _: TaskObj) -> Result<(), SpawnObjError> { + fn spawn_obj(&mut self, _: FutureObj<()>) -> Result<(), SpawnObjError> { Ok(()) } } -- cgit 1.4.1-3-g733a5 From 9f70e7fe3c4126bf8390a78e4740ade3261ac4df Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Sat, 30 Jun 2018 19:37:28 +0200 Subject: Use `From` impls for `FutureObj<()>` --- src/liballoc/boxed.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index c1778ef101a..918ad657be9 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -950,29 +950,29 @@ unsafe impl + 'static> UnsafeFutureObj for PinBox } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> Into> for PinBox { - fn into(self) -> FutureObj { - FutureObj::new(self) +impl + Send + 'static> From> for FutureObj<()> { + fn from(boxed: PinBox) -> Self { + FutureObj::new(boxed) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> Into> for Box { - fn into(self) -> FutureObj { - FutureObj::new(PinBox::from(self)) +impl + Send + 'static> From> for FutureObj<()> { + fn from(boxed: Box) -> Self { + FutureObj::new(PinBox::from(boxed)) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> Into> for PinBox { - fn into(self) -> LocalFutureObj { - LocalFutureObj::new(self) +impl + 'static> From> for LocalFutureObj<()> { + fn from(boxed: PinBox) -> Self { + LocalFutureObj::new(boxed) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> Into> for Box { - fn into(self) -> LocalFutureObj { - LocalFutureObj::new(PinBox::from(self)) +impl + 'static> From> for LocalFutureObj<()> { + fn from(boxed: Box) -> Self { + LocalFutureObj::new(PinBox::from(boxed)) } } -- cgit 1.4.1-3-g733a5 From d8bf2223672973f9d86f6c173793bcfce7890cd8 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Sat, 30 Jun 2018 21:16:44 +0200 Subject: Add lifetime to `FutureObj` --- src/liballoc/boxed.rs | 17 ++--- src/libcore/future.rs | 114 ------------------------------ src/libcore/future/future.rs | 112 +++++++++++++++++++++++++++++ src/libcore/future/future_obj.rs | 149 +++++++++++++++++++++++++++++++++++++++ src/libcore/future/mod.rs | 21 ++++++ src/libcore/task/executor.rs | 8 +-- src/libcore/task/future_obj.rs | 147 -------------------------------------- src/libcore/task/mod.rs | 3 - src/test/run-pass/async-await.rs | 5 +- src/test/run-pass/futures-api.rs | 5 +- 10 files changed, 301 insertions(+), 280 deletions(-) delete mode 100644 src/libcore/future.rs create mode 100644 src/libcore/future/future.rs create mode 100644 src/libcore/future/future_obj.rs create mode 100644 src/libcore/future/mod.rs delete mode 100644 src/libcore/task/future_obj.rs (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 918ad657be9..5984a992afc 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -66,7 +66,8 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll, UnsafeFutureObj, FutureObj, LocalFutureObj}; +use core::future::{FutureObj, LocalFutureObj, UnsafeFutureObj}; +use core::task::{Context, Poll}; use core::convert::From; use raw_vec::RawVec; @@ -915,7 +916,7 @@ impl, U: ?Sized> CoerceUnsized> for PinBox {} impl Unpin for PinBox {} #[unstable(feature = "futures_api", issue = "50547")] -impl<'a, F: ?Sized + Future + Unpin> Future for Box { +impl Future for Box { type Output = F::Output; fn poll(mut self: PinMut, cx: &mut Context) -> Poll { @@ -924,7 +925,7 @@ impl<'a, F: ?Sized + Future + Unpin> Future for Box { } #[unstable(feature = "futures_api", issue = "50547")] -impl<'a, F: ?Sized + Future> Future for PinBox { +impl Future for PinBox { type Output = F::Output; fn poll(mut self: PinMut, cx: &mut Context) -> Poll { @@ -933,7 +934,7 @@ impl<'a, F: ?Sized + Future> Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl + 'static> UnsafeFutureObj for PinBox { +unsafe impl<'a, T, F: Future + 'a> UnsafeFutureObj<'a, T> for PinBox { fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } @@ -950,28 +951,28 @@ unsafe impl + 'static> UnsafeFutureObj for PinBox } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> From> for FutureObj<()> { +impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { fn from(boxed: PinBox) -> Self { FutureObj::new(boxed) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + Send + 'static> From> for FutureObj<()> { +impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { fn from(boxed: Box) -> Self { FutureObj::new(PinBox::from(boxed)) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> From> for LocalFutureObj<()> { +impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { fn from(boxed: PinBox) -> Self { LocalFutureObj::new(boxed) } } #[unstable(feature = "futures_api", issue = "50547")] -impl + 'static> From> for LocalFutureObj<()> { +impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { fn from(boxed: Box) -> Self { LocalFutureObj::new(PinBox::from(boxed)) } diff --git a/src/libcore/future.rs b/src/libcore/future.rs deleted file mode 100644 index 153cd6c0724..00000000000 --- a/src/libcore/future.rs +++ /dev/null @@ -1,114 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] - -//! Asynchronous values. - -use mem::PinMut; -use marker::Unpin; -use task::{self, Poll}; - -/// A future represents an asychronous computation. -/// -/// A future is a value that may not have finished computing yet. This kind of -/// "asynchronous value" makes it possible for a thread to continue doing useful -/// work while it waits for the value to become available. -/// -/// # The `poll` method -/// -/// The core method of future, `poll`, *attempts* to resolve the future into a -/// final value. This method does not block if the value is not ready. Instead, -/// the current task is scheduled to be woken up when it's possible to make -/// further progress by `poll`ing again. The wake up is performed using -/// `cx.waker()`, a handle for waking up the current task. -/// -/// When using a future, you generally won't call `poll` directly, but instead -/// `await!` the value. -pub trait Future { - /// The result of the `Future`. - type Output; - - /// Attempt to resolve the future to a final value, registering - /// the current task for wakeup if the value is not yet available. - /// - /// # Return value - /// - /// This function returns: - /// - /// - [`Poll::Pending`] if the future is not ready yet - /// - [`Poll::Ready(val)`] with the result `val` of this future if it - /// finished successfully. - /// - /// Once a future has finished, clients should not `poll` it again. - /// - /// When a future is not ready yet, `poll` returns - /// `Poll::Pending`. The future will *also* register the - /// interest of the current task in the value being produced. For example, - /// if the future represents the availability of data on a socket, then the - /// task is recorded so that when data arrives, it is woken up (via - /// [`cx.waker()`]). Once a task has been woken up, - /// it should attempt to `poll` the future again, which may or may not - /// produce a final value. - /// - /// Note that if `Pending` is returned it only means that the *current* task - /// (represented by the argument `cx`) will receive a notification. Tasks - /// from previous calls to `poll` will *not* receive notifications. - /// - /// # Runtime characteristics - /// - /// Futures alone are *inert*; they must be *actively* `poll`ed to make - /// progress, meaning that each time the current task is woken up, it should - /// actively re-`poll` pending futures that it still has an interest in. - /// - /// The `poll` function is not called repeatedly in a tight loop for - /// futures, but only whenever the future itself is ready, as signaled via - /// the `Waker` inside `task::Context`. If you're familiar with the - /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures - /// typically do *not* suffer the same problems of "all wakeups must poll - /// all events"; they are more like `epoll(4)`. - /// - /// An implementation of `poll` should strive to return quickly, and must - /// *never* block. Returning quickly prevents unnecessarily clogging up - /// threads or event loops. If it is known ahead of time that a call to - /// `poll` may end up taking awhile, the work should be offloaded to a - /// thread pool (or something similar) to ensure that `poll` can return - /// quickly. - /// - /// # Panics - /// - /// Once a future has completed (returned `Ready` from `poll`), - /// then any future calls to `poll` may panic, block forever, or otherwise - /// cause bad behavior. The `Future` trait itself provides no guarantees - /// about the behavior of `poll` after a future has completed. - /// - /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending - /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready - /// [`cx.waker()`]: ../task/struct.Context.html#method.waker - fn poll(self: PinMut, cx: &mut task::Context) -> Poll; -} - -impl<'a, F: ?Sized + Future + Unpin> Future for &'a mut F { - type Output = F::Output; - - fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { - F::poll(PinMut::new(&mut **self), cx) - } -} - -impl<'a, F: ?Sized + Future> Future for PinMut<'a, F> { - type Output = F::Output; - - fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { - F::poll((*self).reborrow(), cx) - } -} diff --git a/src/libcore/future/future.rs b/src/libcore/future/future.rs new file mode 100644 index 00000000000..10b4ca9b0b2 --- /dev/null +++ b/src/libcore/future/future.rs @@ -0,0 +1,112 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use mem::PinMut; +use marker::Unpin; +use task::{self, Poll}; + +/// A future represents an asychronous computation. +/// +/// A future is a value that may not have finished computing yet. This kind of +/// "asynchronous value" makes it possible for a thread to continue doing useful +/// work while it waits for the value to become available. +/// +/// # The `poll` method +/// +/// The core method of future, `poll`, *attempts* to resolve the future into a +/// final value. This method does not block if the value is not ready. Instead, +/// the current task is scheduled to be woken up when it's possible to make +/// further progress by `poll`ing again. The wake up is performed using +/// `cx.waker()`, a handle for waking up the current task. +/// +/// When using a future, you generally won't call `poll` directly, but instead +/// `await!` the value. +pub trait Future { + /// The result of the `Future`. + type Output; + + /// Attempt to resolve the future to a final value, registering + /// the current task for wakeup if the value is not yet available. + /// + /// # Return value + /// + /// This function returns: + /// + /// - [`Poll::Pending`] if the future is not ready yet + /// - [`Poll::Ready(val)`] with the result `val` of this future if it + /// finished successfully. + /// + /// Once a future has finished, clients should not `poll` it again. + /// + /// When a future is not ready yet, `poll` returns + /// `Poll::Pending`. The future will *also* register the + /// interest of the current task in the value being produced. For example, + /// if the future represents the availability of data on a socket, then the + /// task is recorded so that when data arrives, it is woken up (via + /// [`cx.waker()`]). Once a task has been woken up, + /// it should attempt to `poll` the future again, which may or may not + /// produce a final value. + /// + /// Note that if `Pending` is returned it only means that the *current* task + /// (represented by the argument `cx`) will receive a notification. Tasks + /// from previous calls to `poll` will *not* receive notifications. + /// + /// # Runtime characteristics + /// + /// Futures alone are *inert*; they must be *actively* `poll`ed to make + /// progress, meaning that each time the current task is woken up, it should + /// actively re-`poll` pending futures that it still has an interest in. + /// + /// The `poll` function is not called repeatedly in a tight loop for + /// futures, but only whenever the future itself is ready, as signaled via + /// the `Waker` inside `task::Context`. If you're familiar with the + /// `poll(2)` or `select(2)` syscalls on Unix it's worth noting that futures + /// typically do *not* suffer the same problems of "all wakeups must poll + /// all events"; they are more like `epoll(4)`. + /// + /// An implementation of `poll` should strive to return quickly, and must + /// *never* block. Returning quickly prevents unnecessarily clogging up + /// threads or event loops. If it is known ahead of time that a call to + /// `poll` may end up taking awhile, the work should be offloaded to a + /// thread pool (or something similar) to ensure that `poll` can return + /// quickly. + /// + /// # Panics + /// + /// Once a future has completed (returned `Ready` from `poll`), + /// then any future calls to `poll` may panic, block forever, or otherwise + /// cause bad behavior. The `Future` trait itself provides no guarantees + /// about the behavior of `poll` after a future has completed. + /// + /// [`Poll::Pending`]: ../task/enum.Poll.html#variant.Pending + /// [`Poll::Ready(val)`]: ../task/enum.Poll.html#variant.Ready + /// [`cx.waker()`]: ../task/struct.Context.html#method.waker + fn poll(self: PinMut, cx: &mut task::Context) -> Poll; +} + +impl<'a, F: ?Sized + Future + Unpin> Future for &'a mut F { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { + F::poll(PinMut::new(&mut **self), cx) + } +} + +impl<'a, F: ?Sized + Future> Future for PinMut<'a, F> { + type Output = F::Output; + + fn poll(mut self: PinMut, cx: &mut task::Context) -> Poll { + F::poll((*self).reborrow(), cx) + } +} diff --git a/src/libcore/future/future_obj.rs b/src/libcore/future/future_obj.rs new file mode 100644 index 00000000000..c60b8b97d34 --- /dev/null +++ b/src/libcore/future/future_obj.rs @@ -0,0 +1,149 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +use fmt; +use future::Future; +use marker::PhantomData; +use mem::PinMut; +use task::{Context, Poll}; + +/// A custom trait object for polling futures, roughly akin to +/// `Box>`. +/// Contrary to `FutureObj`, `LocalFutureObj` does not have a `Send` bound. +pub struct LocalFutureObj<'a, T> { + ptr: *mut (), + poll_fn: unsafe fn(*mut (), &mut Context) -> Poll, + drop_fn: unsafe fn(*mut ()), + _marker1: PhantomData, + _marker2: PhantomData<&'a ()>, +} + +impl<'a, T> LocalFutureObj<'a, T> { + /// Create a `LocalFutureObj` from a custom trait object representation. + #[inline] + pub fn new + 'a>(f: F) -> LocalFutureObj<'a, T> { + LocalFutureObj { + ptr: f.into_raw(), + poll_fn: F::poll, + drop_fn: F::drop, + _marker1: PhantomData, + _marker2: PhantomData, + } + } + + /// Converts the `LocalFutureObj` into a `FutureObj` + /// To make this operation safe one has to ensure that the `UnsafeFutureObj` + /// instance from which this `LocalFutureObj` was created actually + /// implements `Send`. + #[inline] + pub unsafe fn as_future_obj(self) -> FutureObj<'a, T> { + FutureObj(self) + } +} + +impl<'a, T> fmt::Debug for LocalFutureObj<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("LocalFutureObj") + .finish() + } +} + +impl<'a, T> From> for LocalFutureObj<'a, T> { + #[inline] + fn from(f: FutureObj<'a, T>) -> LocalFutureObj<'a, T> { + f.0 + } +} + +impl<'a, T> Future for LocalFutureObj<'a, T> { + type Output = T; + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll { + unsafe { + (self.poll_fn)(self.ptr, cx) + } + } +} + +impl<'a, T> Drop for LocalFutureObj<'a, T> { + fn drop(&mut self) { + unsafe { + (self.drop_fn)(self.ptr) + } + } +} + +/// A custom trait object for polling futures, roughly akin to +/// `Box> + Send`. +pub struct FutureObj<'a, T>(LocalFutureObj<'a, T>); + +unsafe impl<'a, T> Send for FutureObj<'a, T> {} + +impl<'a, T> FutureObj<'a, T> { + /// Create a `FutureObj` from a custom trait object representation. + #[inline] + pub fn new + Send>(f: F) -> FutureObj<'a, T> { + FutureObj(LocalFutureObj::new(f)) + } +} + +impl<'a, T> fmt::Debug for FutureObj<'a, T> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + f.debug_struct("FutureObj") + .finish() + } +} + +impl<'a, T> Future for FutureObj<'a, T> { + type Output = T; + + #[inline] + fn poll(self: PinMut, cx: &mut Context) -> Poll { + let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) }; + pinned_field.poll(cx) + } +} + +/// A custom implementation of a future trait object for `FutureObj`, providing +/// a hand-rolled vtable. +/// +/// This custom representation is typically used only in `no_std` contexts, +/// where the default `Box`-based implementation is not available. +/// +/// The implementor must guarantee that it is safe to call `poll` repeatedly (in +/// a non-concurrent fashion) with the result of `into_raw` until `drop` is +/// called. +pub unsafe trait UnsafeFutureObj<'a, T>: 'a { + /// Convert a owned instance into a (conceptually owned) void pointer. + fn into_raw(self) -> *mut (); + + /// Poll the future represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to repeatedly call + /// `poll` with the result of `into_raw` until `drop` is called; such calls + /// are not, however, allowed to race with each other or with calls to `drop`. + unsafe fn poll(future: *mut (), cx: &mut Context) -> Poll; + + /// Drops the future represented by the given void pointer. + /// + /// # Safety + /// + /// The trait implementor must guarantee that it is safe to call this + /// function once per `into_raw` invocation; that call cannot race with + /// other calls to `drop` or `poll`. + unsafe fn drop(future: *mut ()); +} diff --git a/src/libcore/future/mod.rs b/src/libcore/future/mod.rs new file mode 100644 index 00000000000..f9361a0f4e7 --- /dev/null +++ b/src/libcore/future/mod.rs @@ -0,0 +1,21 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +#![unstable(feature = "futures_api", + reason = "futures in libcore are unstable", + issue = "50547")] + +//! Asynchronous values. + +mod future; +pub use self::future::Future; + +mod future_obj; +pub use self::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj}; diff --git a/src/libcore/task/executor.rs b/src/libcore/task/executor.rs index 55ea5e724c1..f1db5093e98 100644 --- a/src/libcore/task/executor.rs +++ b/src/libcore/task/executor.rs @@ -13,7 +13,7 @@ issue = "50547")] use fmt; -use super::{FutureObj, LocalFutureObj}; +use future::{FutureObj, LocalFutureObj}; /// A task executor. /// @@ -29,7 +29,7 @@ pub trait Executor { /// /// The executor may be unable to spawn tasks, either because it has /// been shut down or is resource-constrained. - fn spawn_obj(&mut self, task: FutureObj<()>) -> Result<(), SpawnObjError>; + fn spawn_obj(&mut self, task: FutureObj<'static, ()>) -> Result<(), SpawnObjError>; /// Determine whether the executor is able to spawn new tasks. /// @@ -76,7 +76,7 @@ pub struct SpawnObjError { pub kind: SpawnErrorKind, /// The task for which spawning was attempted - pub task: FutureObj<()>, + pub task: FutureObj<'static, ()>, } /// The result of a failed spawn @@ -86,5 +86,5 @@ pub struct SpawnLocalObjError { pub kind: SpawnErrorKind, /// The task for which spawning was attempted - pub task: LocalFutureObj<()>, + pub task: LocalFutureObj<'static, ()>, } diff --git a/src/libcore/task/future_obj.rs b/src/libcore/task/future_obj.rs deleted file mode 100644 index 3ed3bd51cf6..00000000000 --- a/src/libcore/task/future_obj.rs +++ /dev/null @@ -1,147 +0,0 @@ -// Copyright 2018 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 or the MIT license -// , at your -// option. This file may not be copied, modified, or distributed -// except according to those terms. - -#![unstable(feature = "futures_api", - reason = "futures in libcore are unstable", - issue = "50547")] - -use fmt; -use future::Future; -use marker::PhantomData; -use mem::PinMut; -use task::{Context, Poll}; - -/// A custom trait object for polling futures, roughly akin to -/// `Box>`. -/// Contrary to `FutureObj`, `LocalFutureObj` does not have a `Send` bound. -pub struct LocalFutureObj { - ptr: *mut (), - poll_fn: unsafe fn(*mut (), &mut Context) -> Poll, - drop_fn: unsafe fn(*mut ()), - _marker: PhantomData, -} - -impl LocalFutureObj { - /// Create a `LocalFutureObj` from a custom trait object representation. - #[inline] - pub fn new>(f: F) -> LocalFutureObj { - LocalFutureObj { - ptr: f.into_raw(), - poll_fn: F::poll, - drop_fn: F::drop, - _marker: PhantomData, - } - } - - /// Converts the `LocalFutureObj` into a `FutureObj` - /// To make this operation safe one has to ensure that the `UnsafeFutureObj` - /// instance from which this `LocalFutureObj` was created actually - /// implements `Send`. - #[inline] - pub unsafe fn as_future_obj(self) -> FutureObj { - FutureObj(self) - } -} - -impl fmt::Debug for LocalFutureObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("LocalFutureObj") - .finish() - } -} - -impl From> for LocalFutureObj { - #[inline] - fn from(f: FutureObj) -> LocalFutureObj { - f.0 - } -} - -impl Future for LocalFutureObj { - type Output = T; - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll { - unsafe { - (self.poll_fn)(self.ptr, cx) - } - } -} - -impl Drop for LocalFutureObj { - fn drop(&mut self) { - unsafe { - (self.drop_fn)(self.ptr) - } - } -} - -/// A custom trait object for polling futures, roughly akin to -/// `Box> + Send`. -pub struct FutureObj(LocalFutureObj); - -unsafe impl Send for FutureObj {} - -impl FutureObj { - /// Create a `FutureObj` from a custom trait object representation. - #[inline] - pub fn new + Send>(f: F) -> FutureObj { - FutureObj(LocalFutureObj::new(f)) - } -} - -impl fmt::Debug for FutureObj { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { - f.debug_struct("FutureObj") - .finish() - } -} - -impl Future for FutureObj { - type Output = T; - - #[inline] - fn poll(self: PinMut, cx: &mut Context) -> Poll { - let pinned_field = unsafe { PinMut::map_unchecked(self, |x| &mut x.0) }; - pinned_field.poll(cx) - } -} - -/// A custom implementation of a future trait object for `FutureObj`, providing -/// a hand-rolled vtable. -/// -/// This custom representation is typically used only in `no_std` contexts, -/// where the default `Box`-based implementation is not available. -/// -/// The implementor must guarantee that it is safe to call `poll` repeatedly (in -/// a non-concurrent fashion) with the result of `into_raw` until `drop` is -/// called. -pub unsafe trait UnsafeFutureObj: 'static { - /// Convert a owned instance into a (conceptually owned) void pointer. - fn into_raw(self) -> *mut (); - - /// Poll the future represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to repeatedly call - /// `poll` with the result of `into_raw` until `drop` is called; such calls - /// are not, however, allowed to race with each other or with calls to `drop`. - unsafe fn poll(future: *mut (), cx: &mut Context) -> Poll; - - /// Drops the future represented by the given void pointer. - /// - /// # Safety - /// - /// The trait implementor must guarantee that it is safe to call this - /// function once per `into_raw` invocation; that call cannot race with - /// other calls to `drop` or `poll`. - unsafe fn drop(future: *mut ()); -} diff --git a/src/libcore/task/mod.rs b/src/libcore/task/mod.rs index 06cd7a9dd77..c4f07536164 100644 --- a/src/libcore/task/mod.rs +++ b/src/libcore/task/mod.rs @@ -25,8 +25,5 @@ pub use self::executor::{ mod poll; pub use self::poll::Poll; -mod future_obj; -pub use self::future_obj::{FutureObj, LocalFutureObj, UnsafeFutureObj}; - mod wake; pub use self::wake::{Waker, LocalWaker, UnsafeWake}; diff --git a/src/test/run-pass/async-await.rs b/src/test/run-pass/async-await.rs index 3a67750e77e..0ac37485d3d 100644 --- a/src/test/run-pass/async-await.rs +++ b/src/test/run-pass/async-await.rs @@ -19,9 +19,10 @@ use std::sync::{ Arc, atomic::{self, AtomicUsize}, }; +use std::future::FutureObj; use std::task::{ Context, Poll, Wake, - Executor, FutureObj, SpawnObjError, + Executor, SpawnObjError, local_waker_from_nonlocal, }; @@ -37,7 +38,7 @@ impl Wake for Counter { struct NoopExecutor; impl Executor for NoopExecutor { - fn spawn_obj(&mut self, _: FutureObj) -> Result<(), SpawnObjError> { + fn spawn_obj(&mut self, _: FutureObj<'static, ()>) -> Result<(), SpawnObjError> { Ok(()) } } diff --git a/src/test/run-pass/futures-api.rs b/src/test/run-pass/futures-api.rs index a427b82af6a..6cb975a9560 100644 --- a/src/test/run-pass/futures-api.rs +++ b/src/test/run-pass/futures-api.rs @@ -19,10 +19,11 @@ use std::sync::{ Arc, atomic::{self, AtomicUsize}, }; +use std::future::FutureObj; use std::task::{ Context, Poll, Wake, Waker, LocalWaker, - Executor, FutureObj, SpawnObjError, + Executor, SpawnObjError, local_waker, local_waker_from_nonlocal, }; @@ -44,7 +45,7 @@ impl Wake for Counter { struct NoopExecutor; impl Executor for NoopExecutor { - fn spawn_obj(&mut self, _: FutureObj<()>) -> Result<(), SpawnObjError> { + fn spawn_obj(&mut self, _: FutureObj<'static, ()>) -> Result<(), SpawnObjError> { Ok(()) } } -- cgit 1.4.1-3-g733a5 From 042928f0f591a63e0a2ed31c932015f4248d4fa7 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Sun, 1 Jul 2018 09:28:16 +0200 Subject: `UnsafeFutureObj` impl for `PinMut` --- src/liballoc/boxed.rs | 13 ++++++------- src/libcore/future/future_obj.rs | 6 +++--- src/libcore/mem.rs | 17 +++++++++++++++++ 3 files changed, 26 insertions(+), 10 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 5984a992afc..7f6d27088b7 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -58,17 +58,16 @@ use core::any::Any; use core::borrow; use core::cmp::Ordering; +use core::convert::From; use core::fmt; -use core::future::Future; +use core::future::{Future, FutureObj, LocalFutureObj, UnsafeFutureObj}; use core::hash::{Hash, Hasher}; use core::iter::FusedIterator; use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::future::{FutureObj, LocalFutureObj, UnsafeFutureObj}; use core::task::{Context, Poll}; -use core::convert::From; use raw_vec::RawVec; use str::from_boxed_utf8_unchecked; @@ -939,14 +938,14 @@ unsafe impl<'a, T, F: Future + 'a> UnsafeFutureObj<'a, T> for PinBox PinBox::into_raw(self) as *mut () } - unsafe fn poll(task: *mut (), cx: &mut Context) -> Poll { - let ptr = task as *mut F; + unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll { + let ptr = ptr as *mut F; let pin: PinMut = PinMut::new_unchecked(&mut *ptr); pin.poll(cx) } - unsafe fn drop(task: *mut ()) { - drop(PinBox::from_raw(task as *mut F)) + unsafe fn drop(ptr: *mut ()) { + drop(PinBox::from_raw(ptr as *mut F)) } } diff --git a/src/libcore/future/future_obj.rs b/src/libcore/future/future_obj.rs index c60b8b97d34..67bd3de98c1 100644 --- a/src/libcore/future/future_obj.rs +++ b/src/libcore/future/future_obj.rs @@ -126,7 +126,7 @@ impl<'a, T> Future for FutureObj<'a, T> { /// a non-concurrent fashion) with the result of `into_raw` until `drop` is /// called. pub unsafe trait UnsafeFutureObj<'a, T>: 'a { - /// Convert a owned instance into a (conceptually owned) void pointer. + /// Convert an owned instance into a (conceptually owned) void pointer. fn into_raw(self) -> *mut (); /// Poll the future represented by the given void pointer. @@ -136,7 +136,7 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { /// The trait implementor must guarantee that it is safe to repeatedly call /// `poll` with the result of `into_raw` until `drop` is called; such calls /// are not, however, allowed to race with each other or with calls to `drop`. - unsafe fn poll(future: *mut (), cx: &mut Context) -> Poll; + unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll; /// Drops the future represented by the given void pointer. /// @@ -145,5 +145,5 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { /// The trait implementor must guarantee that it is safe to call this /// function once per `into_raw` invocation; that call cannot race with /// other calls to `drop` or `poll`. - unsafe fn drop(future: *mut ()); + unsafe fn drop(ptr: *mut ()); } diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index 08bd9289ab4..5bc55300a97 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -18,10 +18,12 @@ use clone; use cmp; use fmt; +use future::{Future, UnsafeFutureObj}; use hash; use intrinsics; use marker::{Copy, PhantomData, Sized, Unpin, Unsize}; use ptr; +use task::{Context, Poll}; use ops::{Deref, DerefMut, CoerceUnsized}; #[stable(feature = "rust1", since = "1.0.0")] @@ -1227,3 +1229,18 @@ impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for PinM #[unstable(feature = "pin", issue = "49150")] impl<'a, T: ?Sized> Unpin for PinMut<'a, T> {} + +#[unstable(feature = "futures_api", issue = "50547")] +unsafe impl<'a, T, F: Future + 'a> UnsafeFutureObj<'a, T> for PinMut<'a, F> { + fn into_raw(self) -> *mut () { + unsafe { PinMut::get_mut_unchecked(self) as *mut F as *mut () } + } + + unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll { + PinMut::new_unchecked(&mut *(ptr as *mut F)).poll(cx) + } + + unsafe fn drop(ptr: *mut ()) { + drop(PinMut::new_unchecked(&mut *(ptr as *mut F))); + } +} -- cgit 1.4.1-3-g733a5 From 59f2edbf1adfea256216cb7fb65d291d324ea857 Mon Sep 17 00:00:00 2001 From: Niko Matsakis Date: Tue, 20 Feb 2018 10:26:48 -0500 Subject: add outlives annotations to `BTreeMap` nll requires these annotations, I believe because of https://github.com/rust-lang/rust/issues/29149 --- src/liballoc/collections/btree/map.rs | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/collections/btree/map.rs b/src/liballoc/collections/btree/map.rs index 2aad3149bb2..8c950cd06d9 100644 --- a/src/liballoc/collections/btree/map.rs +++ b/src/liballoc/collections/btree/map.rs @@ -149,12 +149,11 @@ unsafe impl<#[may_dangle] K, #[may_dangle] V> Drop for BTreeMap { #[stable(feature = "rust1", since = "1.0.0")] impl Clone for BTreeMap { fn clone(&self) -> BTreeMap { - fn clone_subtree(node: node::NodeRef) - -> BTreeMap { - + fn clone_subtree<'a, K: Clone, V: Clone>( + node: node::NodeRef, K, V, marker::LeafOrInternal> + ) -> BTreeMap + where K: 'a, V: 'a, + { match node.force() { Leaf(leaf) => { let mut out_tree = BTreeMap { @@ -1080,7 +1079,11 @@ impl BTreeMap { /// Calculates the number of elements if it is incorrect. fn recalc_length(&mut self) { - fn dfs(node: NodeRef) -> usize { + fn dfs<'a, K, V>( + node: NodeRef, K, V, marker::LeafOrInternal> + ) -> usize + where K: 'a, V: 'a + { let mut res = node.len(); if let Internal(node) = node.force() { -- cgit 1.4.1-3-g733a5 From ae408947de1311f9673d0ae34028933cd191ac90 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Mon, 2 Jul 2018 19:07:59 +0200 Subject: Implement `UnsafeFutureObj` for `&mut Future` --- src/liballoc/boxed.rs | 4 +++- src/libcore/future/future_obj.rs | 16 +++++++++++++++- src/libcore/mem.rs | 4 +++- 3 files changed, 21 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 7f6d27088b7..fabeaa1c144 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -933,7 +933,9 @@ impl Future for PinBox { } #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl<'a, T, F: Future + 'a> UnsafeFutureObj<'a, T> for PinBox { +unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for PinBox + where F: Future + 'a +{ fn into_raw(self) -> *mut () { PinBox::into_raw(self) as *mut () } diff --git a/src/libcore/future/future_obj.rs b/src/libcore/future/future_obj.rs index 99f21272538..98c504a3f7b 100644 --- a/src/libcore/future/future_obj.rs +++ b/src/libcore/future/future_obj.rs @@ -14,7 +14,7 @@ use fmt; use future::Future; -use marker::PhantomData; +use marker::{PhantomData, Unpin}; use mem::PinMut; use task::{Context, Poll}; @@ -163,3 +163,17 @@ pub unsafe trait UnsafeFutureObj<'a, T>: 'a { /// other calls to `drop` or `poll`. unsafe fn drop(ptr: *mut ()); } + +unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for &'a mut F + where F: Future + Unpin + 'a +{ + fn into_raw(self) -> *mut () { + self as *mut F as *mut () + } + + unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll { + PinMut::new_unchecked(&mut *(ptr as *mut F)).poll(cx) + } + + unsafe fn drop(_ptr: *mut ()) {} +} diff --git a/src/libcore/mem.rs b/src/libcore/mem.rs index b83c2e21a1a..84173654655 100644 --- a/src/libcore/mem.rs +++ b/src/libcore/mem.rs @@ -1231,7 +1231,9 @@ impl<'a, T: ?Sized + Unsize, U: ?Sized> CoerceUnsized> for PinM impl<'a, T: ?Sized> Unpin for PinMut<'a, T> {} #[unstable(feature = "futures_api", issue = "50547")] -unsafe impl<'a, T, F: Future + 'a> UnsafeFutureObj<'a, T> for PinMut<'a, F> { +unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for PinMut<'a, F> + where F: Future + 'a +{ fn into_raw(self) -> *mut () { unsafe { PinMut::get_mut_unchecked(self) as *mut F as *mut () } } -- cgit 1.4.1-3-g733a5 From e666c2bd0742cbf88ff9fa26cfc194099a139589 Mon Sep 17 00:00:00 2001 From: Josef Reinhard Brandl Date: Mon, 2 Jul 2018 19:21:32 +0200 Subject: Implemented `UnsafeFutureObj` on `Box` --- src/liballoc/boxed.rs | 23 +++++++++++++++++++++-- 1 file changed, 21 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fabeaa1c144..fb16bdf0ab4 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -932,6 +932,25 @@ impl Future for PinBox { } } +#[unstable(feature = "futures_api", issue = "50547")] +unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for Box + where F: Future + 'a +{ + fn into_raw(self) -> *mut () { + Box::into_raw(self) as *mut () + } + + unsafe fn poll(ptr: *mut (), cx: &mut Context) -> Poll { + let ptr = ptr as *mut F; + let pin: PinMut = PinMut::new_unchecked(&mut *ptr); + pin.poll(cx) + } + + unsafe fn drop(ptr: *mut ()) { + drop(Box::from_raw(ptr as *mut F)) + } +} + #[unstable(feature = "futures_api", issue = "50547")] unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for PinBox where F: Future + 'a @@ -961,7 +980,7 @@ impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ( #[unstable(feature = "futures_api", issue = "50547")] impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { fn from(boxed: Box) -> Self { - FutureObj::new(PinBox::from(boxed)) + FutureObj::new(boxed) } } @@ -975,6 +994,6 @@ impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> #[unstable(feature = "futures_api", issue = "50547")] impl<'a, F: Future + 'a> From> for LocalFutureObj<'a, ()> { fn from(boxed: Box) -> Self { - LocalFutureObj::new(PinBox::from(boxed)) + LocalFutureObj::new(boxed) } } -- cgit 1.4.1-3-g733a5 From f96c2468695911222ba7557ce04af0dd8fbb6df2 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Tue, 3 Jul 2018 22:10:30 +0200 Subject: Strenghten synchronization in `Arc::is_unique` Previously, `is_unique` would not synchronize at all with a `drop` that returned early because it was not the last reference, leading to a data race. Fixes #51780 --- src/liballoc/sync.rs | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 2abd9c85c57..4244b09b18f 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -886,13 +886,14 @@ impl Arc { // holder. // // The acquire label here ensures a happens-before relationship with any - // writes to `strong` prior to decrements of the `weak` count (via drop, - // which uses Release). + // writes to `strong` (in particular in `Weak::upgrade`) prior to decrements + // of the `weak` count (via `Weak::drop`, which uses release). If the upgraded + // weak ref was never dropped, the CAS here will fail so we do not care to synchronize. if self.inner().weak.compare_exchange(1, usize::MAX, Acquire, Relaxed).is_ok() { - // Due to the previous acquire read, this will observe any writes to - // `strong` that were due to upgrading weak pointers; only strong - // clones remain, which require that the strong count is > 1 anyway. - let unique = self.inner().strong.load(Relaxed) == 1; + // This needs to be an `Acquire` to synchronize with the decrement of the `strong` + // counter in `drop` -- the only access that happens when any but the last reference + // is being dropped. + let unique = self.inner().strong.load(Acquire) == 1; // The release write here synchronizes with a read in `downgrade`, // effectively preventing the above read of `strong` from happening -- cgit 1.4.1-3-g733a5 From bbf688a84de7001d033764b848a50cbad42f3d5a Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Sat, 30 Jun 2018 14:56:08 -0500 Subject: enable Atomic*.{load,store} for ARMv6-M / MSP430 closes #45085 this commit adds an `atomic_cas` target option and an unstable `#[cfg(target_has_atomic_cas)]` attribute to enable a subset of the `Atomic*` API on architectures that don't support atomic CAS natively, like MSP430 and ARMv6-M. --- src/liballoc/lib.rs | 4 +++- src/liballoc/task.rs | 9 ++++++--- src/libcore/lib.rs | 1 + src/libcore/sync/atomic.rs | 16 ++++++++++++++++ src/librustc/session/config.rs | 4 ++++ src/librustc_target/spec/mod.rs | 6 ++++++ src/librustc_target/spec/msp430_none_elf.rs | 6 ++++-- src/librustc_target/spec/thumbv6m_none_eabi.rs | 2 +- src/libsyntax/feature_gate.rs | 4 ++++ 9 files changed, 45 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 493448eaf88..66bf8de1993 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -86,6 +86,7 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] +#![cfg_attr(not(stage0), feature(cfg_target_has_atomic_cas))] #![feature(coerce_unsized)] #![feature(collections_range)] #![feature(const_fn)] @@ -162,7 +163,8 @@ mod boxed { #[cfg(test)] mod boxed_test; pub mod collections; -#[cfg(target_has_atomic = "ptr")] +#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] +#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] pub mod sync; pub mod rc; pub mod raw_vec; diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs index f14fe3a20da..c8e3e770ed2 100644 --- a/src/liballoc/task.rs +++ b/src/liballoc/task.rs @@ -12,10 +12,12 @@ pub use core::task::*; -#[cfg(target_has_atomic = "ptr")] +#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] +#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] pub use self::if_arc::*; -#[cfg(target_has_atomic = "ptr")] +#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] +#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] mod if_arc { use super::*; use core::marker::PhantomData; @@ -47,7 +49,8 @@ mod if_arc { } } - #[cfg(target_has_atomic = "ptr")] + #[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] + #[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] struct ArcWrapped(PhantomData); unsafe impl UnsafeWake for ArcWrapped { diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index b2b38820a89..fe328bdd107 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -79,6 +79,7 @@ #![feature(associated_type_defaults)] #![feature(attr_literals)] #![feature(cfg_target_has_atomic)] +#![cfg_attr(not(stage0), feature(cfg_target_has_atomic_cas))] #![feature(concat_idents)] #![feature(const_fn)] #![feature(const_int_ops)] diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 7aba8b51cff..647bf4fb40a 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -371,6 +371,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn swap(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } } @@ -401,6 +402,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -446,6 +448,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_exchange(&self, current: bool, new: bool, @@ -537,6 +540,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_and(self.v.get(), val as u8, order) != 0 } } @@ -568,6 +572,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { // We can't use atomic_nand here because it can result in a bool with // an invalid value. This happens because the atomic operation is done @@ -610,6 +615,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_or(self.v.get(), val as u8, order) != 0 } } @@ -640,6 +646,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_xor(self.v.get(), val as u8, order) != 0 } } @@ -786,6 +793,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T } } @@ -815,6 +823,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -853,6 +862,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_exchange(&self, current: *mut T, new: *mut T, @@ -1138,6 +1148,7 @@ assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); ```"), #[inline] #[$stable] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_swap(self.v.get(), val, order) } } @@ -1170,6 +1181,7 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10); ```"), #[inline] #[$stable] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_and_swap(&self, current: $int_type, new: $int_type, @@ -1223,6 +1235,7 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10); ```"), #[inline] #[$stable_cxchg] + #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] pub fn compare_exchange(&self, current: $int_type, new: $int_type, @@ -1677,6 +1690,7 @@ atomic_int!{ } #[inline] +#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] fn strongest_failure_ordering(order: Ordering) -> Ordering { match order { Release => Relaxed, @@ -1713,6 +1727,7 @@ unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { } #[inline] +#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xchg_acq(dst, val), @@ -1751,6 +1766,7 @@ unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { } #[inline] +#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] unsafe fn atomic_compare_exchange(dst: *mut T, old: T, new: T, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index f97e11ef72f..93bfe1fc638 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1367,6 +1367,7 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { let vendor = &sess.target.target.target_vendor; let min_atomic_width = sess.target.target.min_atomic_width(); let max_atomic_width = sess.target.target.max_atomic_width(); + let atomic_cas = sess.target.target.options.atomic_cas; let mut ret = HashSet::new(); // Target bindings. @@ -1406,6 +1407,9 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { } } } + if atomic_cas { + ret.insert((Symbol::intern("target_has_atomic_cas"), None)); + } if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); } diff --git a/src/librustc_target/spec/mod.rs b/src/librustc_target/spec/mod.rs index e54cd773123..8ebf5f7c64d 100644 --- a/src/librustc_target/spec/mod.rs +++ b/src/librustc_target/spec/mod.rs @@ -572,6 +572,9 @@ pub struct TargetOptions { /// Don't use this field; instead use the `.max_atomic_width()` method. pub max_atomic_width: Option, + /// Whether the target supports atomic CAS operations natively + pub atomic_cas: bool, + /// Panic strategy: "unwind" or "abort" pub panic_strategy: PanicStrategy, @@ -690,6 +693,7 @@ impl Default for TargetOptions { no_integrated_as: false, min_atomic_width: None, max_atomic_width: None, + atomic_cas: true, panic_strategy: PanicStrategy::Unwind, abi_blacklist: vec![], crt_static_allows_dylibs: false, @@ -946,6 +950,7 @@ impl Target { key!(no_integrated_as, bool); key!(max_atomic_width, Option); key!(min_atomic_width, Option); + key!(atomic_cas, bool); try!(key!(panic_strategy, PanicStrategy)); key!(crt_static_allows_dylibs, bool); key!(crt_static_default, bool); @@ -1154,6 +1159,7 @@ impl ToJson for Target { target_option_val!(no_integrated_as); target_option_val!(min_atomic_width); target_option_val!(max_atomic_width); + target_option_val!(atomic_cas); target_option_val!(panic_strategy); target_option_val!(crt_static_allows_dylibs); target_option_val!(crt_static_default); diff --git a/src/librustc_target/spec/msp430_none_elf.rs b/src/librustc_target/spec/msp430_none_elf.rs index ce42a908b0e..291511dd429 100644 --- a/src/librustc_target/spec/msp430_none_elf.rs +++ b/src/librustc_target/spec/msp430_none_elf.rs @@ -34,9 +34,11 @@ pub fn target() -> TargetResult { linker: Some("msp430-elf-gcc".to_string()), no_integrated_as: true, - // There are no atomic instructions available in the MSP430 + // There are no atomic CAS instructions available in the MSP430 // instruction set - max_atomic_width: Some(0), + max_atomic_width: Some(16), + + atomic_cas: false, // Because these devices have very little resources having an // unwinder is too onerous so we default to "abort" because the diff --git a/src/librustc_target/spec/thumbv6m_none_eabi.rs b/src/librustc_target/spec/thumbv6m_none_eabi.rs index 9fea07c36f4..0c45178b47a 100644 --- a/src/librustc_target/spec/thumbv6m_none_eabi.rs +++ b/src/librustc_target/spec/thumbv6m_none_eabi.rs @@ -31,7 +31,7 @@ pub fn target() -> TargetResult { features: "+strict-align".to_string(), // There are no atomic instructions available in the instruction set of the ARMv6-M // architecture - max_atomic_width: Some(0), + atomic_cas: false, .. super::thumb_base::opts() } }) diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 2ae0e669fd0..59418f8bf2a 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -479,6 +479,9 @@ declare_features! ( // Allows async and await syntax (active, async_await, "1.28.0", Some(50547), None), + + // Allows async and await syntax + (active, cfg_target_has_atomic_cas, "1.28.0", Some(0), None), ); declare_features! ( @@ -1099,6 +1102,7 @@ const GATED_CFGS: &[(&str, &str, fn(&Features) -> bool)] = &[ ("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)), ("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)), ("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)), + ("target_has_atomic_cas", "cfg_target_has_atomic_cas", cfg_fn!(cfg_target_has_atomic_cas)), ]; #[derive(Debug, Eq, PartialEq)] -- cgit 1.4.1-3-g733a5 From 0ed32313a223dbe1a1d5f61cd66d533992e26f6d Mon Sep 17 00:00:00 2001 From: Jorge Aparicio Date: Thu, 5 Jul 2018 16:44:13 -0500 Subject: #[cfg(target_has_atomic_cas)] -> #[cfg(target_has_atomic = "cas")] --- src/liballoc/lib.rs | 7 +++--- src/liballoc/task.rs | 18 ++++++++++----- src/libcore/lib.rs | 1 - src/libcore/sync/atomic.rs | 32 +++++++++++++------------- src/librustc/session/config.rs | 2 +- src/librustc_target/spec/msp430_none_elf.rs | 1 - src/librustc_target/spec/thumbv6m_none_eabi.rs | 2 +- src/libsyntax/feature_gate.rs | 4 ---- 8 files changed, 34 insertions(+), 33 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 66bf8de1993..35bf8d1b792 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -86,7 +86,6 @@ #![feature(box_patterns)] #![feature(box_syntax)] #![feature(cfg_target_has_atomic)] -#![cfg_attr(not(stage0), feature(cfg_target_has_atomic_cas))] #![feature(coerce_unsized)] #![feature(collections_range)] #![feature(const_fn)] @@ -163,8 +162,10 @@ mod boxed { #[cfg(test)] mod boxed_test; pub mod collections; -#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] -#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] +#[cfg(any( + all(stage0, target_has_atomic = "ptr"), + all(not(stage0), target_has_atomic = "ptr", target_has_atomic = "cas") +))] pub mod sync; pub mod rc; pub mod raw_vec; diff --git a/src/liballoc/task.rs b/src/liballoc/task.rs index c8e3e770ed2..9792d52dd66 100644 --- a/src/liballoc/task.rs +++ b/src/liballoc/task.rs @@ -12,12 +12,16 @@ pub use core::task::*; -#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] -#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] +#[cfg(any( + all(stage0, target_has_atomic = "ptr"), + all(not(stage0), target_has_atomic = "ptr", target_has_atomic = "cas") +))] pub use self::if_arc::*; -#[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] -#[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] +#[cfg(any( + all(stage0, target_has_atomic = "ptr"), + all(not(stage0), target_has_atomic = "ptr", target_has_atomic = "cas") +))] mod if_arc { use super::*; use core::marker::PhantomData; @@ -49,8 +53,10 @@ mod if_arc { } } - #[cfg_attr(stage0, cfg(target_has_atomic = "ptr"))] - #[cfg_attr(not(stage0), cfg(all(target_has_atomic = "ptr", target_has_atomic_cas)))] + #[cfg(any( + all(stage0, target_has_atomic = "ptr"), + all(not(stage0), target_has_atomic = "ptr", target_has_atomic = "cas") + ))] struct ArcWrapped(PhantomData); unsafe impl UnsafeWake for ArcWrapped { diff --git a/src/libcore/lib.rs b/src/libcore/lib.rs index fe328bdd107..b2b38820a89 100644 --- a/src/libcore/lib.rs +++ b/src/libcore/lib.rs @@ -79,7 +79,6 @@ #![feature(associated_type_defaults)] #![feature(attr_literals)] #![feature(cfg_target_has_atomic)] -#![cfg_attr(not(stage0), feature(cfg_target_has_atomic_cas))] #![feature(concat_idents)] #![feature(const_fn)] #![feature(const_int_ops)] diff --git a/src/libcore/sync/atomic.rs b/src/libcore/sync/atomic.rs index 647bf4fb40a..e9d1fb89115 100644 --- a/src/libcore/sync/atomic.rs +++ b/src/libcore/sync/atomic.rs @@ -371,7 +371,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn swap(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_swap(self.v.get(), val as u8, order) != 0 } } @@ -402,7 +402,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_and_swap(&self, current: bool, new: bool, order: Ordering) -> bool { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -448,7 +448,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_exchange(&self, current: bool, new: bool, @@ -540,7 +540,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn fetch_and(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_and(self.v.get(), val as u8, order) != 0 } } @@ -572,7 +572,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn fetch_nand(&self, val: bool, order: Ordering) -> bool { // We can't use atomic_nand here because it can result in a bool with // an invalid value. This happens because the atomic operation is done @@ -615,7 +615,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn fetch_or(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_or(self.v.get(), val as u8, order) != 0 } } @@ -646,7 +646,7 @@ impl AtomicBool { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn fetch_xor(&self, val: bool, order: Ordering) -> bool { unsafe { atomic_xor(self.v.get(), val as u8, order) != 0 } } @@ -793,7 +793,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn swap(&self, ptr: *mut T, order: Ordering) -> *mut T { unsafe { atomic_swap(self.p.get() as *mut usize, ptr as usize, order) as *mut T } } @@ -823,7 +823,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "rust1", since = "1.0.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_and_swap(&self, current: *mut T, new: *mut T, order: Ordering) -> *mut T { match self.compare_exchange(current, new, order, strongest_failure_ordering(order)) { Ok(x) => x, @@ -862,7 +862,7 @@ impl AtomicPtr { /// ``` #[inline] #[stable(feature = "extended_compare_and_swap", since = "1.10.0")] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_exchange(&self, current: *mut T, new: *mut T, @@ -1148,7 +1148,7 @@ assert_eq!(some_var.swap(10, Ordering::Relaxed), 5); ```"), #[inline] #[$stable] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn swap(&self, val: $int_type, order: Ordering) -> $int_type { unsafe { atomic_swap(self.v.get(), val, order) } } @@ -1181,7 +1181,7 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10); ```"), #[inline] #[$stable] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_and_swap(&self, current: $int_type, new: $int_type, @@ -1235,7 +1235,7 @@ assert_eq!(some_var.load(Ordering::Relaxed), 10); ```"), #[inline] #[$stable_cxchg] - #[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] + #[cfg(any(stage0, target_has_atomic = "cas"))] pub fn compare_exchange(&self, current: $int_type, new: $int_type, @@ -1690,7 +1690,7 @@ atomic_int!{ } #[inline] -#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] +#[cfg(any(stage0, target_has_atomic = "cas"))] fn strongest_failure_ordering(order: Ordering) -> Ordering { match order { Release => Relaxed, @@ -1727,7 +1727,7 @@ unsafe fn atomic_load(dst: *const T, order: Ordering) -> T { } #[inline] -#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] +#[cfg(any(stage0, target_has_atomic = "cas"))] unsafe fn atomic_swap(dst: *mut T, val: T, order: Ordering) -> T { match order { Acquire => intrinsics::atomic_xchg_acq(dst, val), @@ -1766,7 +1766,7 @@ unsafe fn atomic_sub(dst: *mut T, val: T, order: Ordering) -> T { } #[inline] -#[cfg_attr(not(stage0), cfg(target_has_atomic_cas))] +#[cfg(any(stage0, target_has_atomic = "cas"))] unsafe fn atomic_compare_exchange(dst: *mut T, old: T, new: T, diff --git a/src/librustc/session/config.rs b/src/librustc/session/config.rs index 93bfe1fc638..342799d41bb 100644 --- a/src/librustc/session/config.rs +++ b/src/librustc/session/config.rs @@ -1408,7 +1408,7 @@ pub fn default_configuration(sess: &Session) -> ast::CrateConfig { } } if atomic_cas { - ret.insert((Symbol::intern("target_has_atomic_cas"), None)); + ret.insert((Symbol::intern("target_has_atomic"), Some(Symbol::intern("cas")))); } if sess.opts.debug_assertions { ret.insert((Symbol::intern("debug_assertions"), None)); diff --git a/src/librustc_target/spec/msp430_none_elf.rs b/src/librustc_target/spec/msp430_none_elf.rs index 291511dd429..3ac4c459c63 100644 --- a/src/librustc_target/spec/msp430_none_elf.rs +++ b/src/librustc_target/spec/msp430_none_elf.rs @@ -37,7 +37,6 @@ pub fn target() -> TargetResult { // There are no atomic CAS instructions available in the MSP430 // instruction set max_atomic_width: Some(16), - atomic_cas: false, // Because these devices have very little resources having an diff --git a/src/librustc_target/spec/thumbv6m_none_eabi.rs b/src/librustc_target/spec/thumbv6m_none_eabi.rs index 0c45178b47a..26812501814 100644 --- a/src/librustc_target/spec/thumbv6m_none_eabi.rs +++ b/src/librustc_target/spec/thumbv6m_none_eabi.rs @@ -29,7 +29,7 @@ pub fn target() -> TargetResult { // The ARMv6-M architecture doesn't support unaligned loads/stores so we disable them // with +strict-align. features: "+strict-align".to_string(), - // There are no atomic instructions available in the instruction set of the ARMv6-M + // There are no atomic CAS instructions available in the instruction set of the ARMv6-M // architecture atomic_cas: false, .. super::thumb_base::opts() diff --git a/src/libsyntax/feature_gate.rs b/src/libsyntax/feature_gate.rs index 59418f8bf2a..2ae0e669fd0 100644 --- a/src/libsyntax/feature_gate.rs +++ b/src/libsyntax/feature_gate.rs @@ -479,9 +479,6 @@ declare_features! ( // Allows async and await syntax (active, async_await, "1.28.0", Some(50547), None), - - // Allows async and await syntax - (active, cfg_target_has_atomic_cas, "1.28.0", Some(0), None), ); declare_features! ( @@ -1102,7 +1099,6 @@ const GATED_CFGS: &[(&str, &str, fn(&Features) -> bool)] = &[ ("target_vendor", "cfg_target_vendor", cfg_fn!(cfg_target_vendor)), ("target_thread_local", "cfg_target_thread_local", cfg_fn!(cfg_target_thread_local)), ("target_has_atomic", "cfg_target_has_atomic", cfg_fn!(cfg_target_has_atomic)), - ("target_has_atomic_cas", "cfg_target_has_atomic_cas", cfg_fn!(cfg_target_has_atomic_cas)), ]; #[derive(Debug, Eq, PartialEq)] -- cgit 1.4.1-3-g733a5 From 7fbc3895e359b9212bc0a78692198f15bbe462b5 Mon Sep 17 00:00:00 2001 From: Thayne McCombs Date: Fri, 6 Jul 2018 01:00:40 -0600 Subject: Stabilize rc_downcast Fixes #44608 --- src/liballoc/rc.rs | 3 +-- src/liballoc/sync.rs | 3 +-- 2 files changed, 2 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 32d624e8fbc..357bc8ba2dd 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -620,13 +620,12 @@ impl Rc { impl Rc { #[inline] - #[unstable(feature = "rc_downcast", issue = "44608")] + #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Rc` to a concrete type. /// /// # Examples /// /// ``` - /// #![feature(rc_downcast)] /// use std::any::Any; /// use std::rc::Rc; /// diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 2abd9c85c57..dcc3560a070 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -981,13 +981,12 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { impl Arc { #[inline] - #[unstable(feature = "rc_downcast", issue = "44608")] + #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Arc` to a concrete type. /// /// # Examples /// /// ``` - /// #![feature(rc_downcast)] /// use std::any::Any; /// use std::sync::Arc; /// -- cgit 1.4.1-3-g733a5 From ad7621d42ee90143cd15715cc546177575fd5844 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Fri, 6 Jul 2018 17:20:39 +0200 Subject: Handle array manually in string case conversion methods --- src/liballoc/str.rs | 29 +++++++++++++++++++++++++++-- src/libcore/unicode/mod.rs | 3 +++ 2 files changed, 30 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/str.rs b/src/liballoc/str.rs index bb99d0401d3..4d6434c378e 100644 --- a/src/liballoc/str.rs +++ b/src/liballoc/str.rs @@ -45,6 +45,7 @@ use core::str::pattern::{Searcher, ReverseSearcher, DoubleEndedSearcher}; use core::mem; use core::ptr; use core::iter::FusedIterator; +use core::unicode::conversions; use borrow::{Borrow, ToOwned}; use boxed::Box; @@ -369,7 +370,18 @@ impl str { // See https://github.com/rust-lang/rust/issues/26035 map_uppercase_sigma(self, i, &mut s) } else { - s.extend(c.to_lowercase()); + match conversions::to_lower(c) { + [a, '\0', _] => s.push(a), + [a, b, '\0'] => { + s.push(a); + s.push(b); + } + [a, b, c] => { + s.push(a); + s.push(b); + s.push(c); + } + } } } return s; @@ -423,7 +435,20 @@ impl str { #[stable(feature = "unicode_case_mapping", since = "1.2.0")] pub fn to_uppercase(&self) -> String { let mut s = String::with_capacity(self.len()); - s.extend(self.chars().flat_map(|c| c.to_uppercase())); + for c in self[..].chars() { + match conversions::to_upper(c) { + [a, '\0', _] => s.push(a), + [a, b, '\0'] => { + s.push(a); + s.push(b); + } + [a, b, c] => { + s.push(a); + s.push(b); + s.push(c); + } + } + } return s; } diff --git a/src/libcore/unicode/mod.rs b/src/libcore/unicode/mod.rs index b6b033adc04..e5cda880f88 100644 --- a/src/libcore/unicode/mod.rs +++ b/src/libcore/unicode/mod.rs @@ -20,6 +20,9 @@ pub(crate) mod version; pub mod derived_property { pub use unicode::tables::derived_property::{Case_Ignorable, Cased}; } +pub mod conversions { + pub use unicode::tables::conversions::{to_lower, to_upper}; +} // For use in libsyntax pub mod property { -- cgit 1.4.1-3-g733a5 From 6e2c49ff0e61e13aa3381eefba7923672a3c085f Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 29 Jun 2018 15:43:34 +0200 Subject: Use an aligned dangling pointer in Weak::new, rather than address 1 --- src/liballoc/sync.rs | 50 +++++++++++++++++++++++++++++--------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 4244b09b18f..abc0befeb94 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -43,9 +43,6 @@ use vec::Vec; /// necessarily) at _exactly_ `MAX_REFCOUNT + 1` references. const MAX_REFCOUNT: usize = (isize::MAX) as usize; -/// A sentinel value that is used for the pointer of `Weak::new()`. -const WEAK_EMPTY: usize = 1; - /// A thread-safe reference-counting pointer. 'Arc' stands for 'Atomically /// Reference Counted'. /// @@ -239,9 +236,9 @@ impl, U: ?Sized> CoerceUnsized> for Arc {} #[stable(feature = "arc_weak", since = "1.4.0")] pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, - // but it is actually not truly "non-null". A `Weak::new()` will set this - // to a sentinel value, instead of needing to allocate some space in the - // heap. + // but it is not necessarily a valid pointer. + // `Weak::new` sets this to a dangling pointer so that it doesn’t need + // to allocate space on the heap. ptr: NonNull>, } @@ -1035,14 +1032,18 @@ impl Weak { /// ``` #[stable(feature = "downgraded_weak", since = "1.10.0")] pub fn new() -> Weak { - unsafe { - Weak { - ptr: NonNull::new_unchecked(WEAK_EMPTY as *mut _), - } + Weak { + ptr: NonNull::dangling(), } } } +fn is_dangling(ptr: NonNull) -> bool { + let address = ptr.as_ptr() as *mut () as usize; + let align = align_of_val(unsafe { ptr.as_ref() }); + address == align +} + impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending /// the lifetime of the value if successful. @@ -1074,11 +1075,7 @@ impl Weak { pub fn upgrade(&self) -> Option> { // We use a CAS loop to increment the strong count instead of a // fetch_add because once the count hits 0 it must never be above 0. - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return None; - } else { - unsafe { self.ptr.as_ref() } - }; + let inner = self.inner()?; // Relaxed load because any write of 0 that we can observe // leaves the field in a permanently zero state (so a @@ -1109,6 +1106,17 @@ impl Weak { } } } + + /// Return `None` when the pointer is dangling and there is no allocated `ArcInner`, + /// i.e. this `Weak` was created by `Weak::new` + #[inline] + fn inner(&self) -> Option<&ArcInner> { + if is_dangling(self.ptr) { + None + } else { + Some(unsafe { self.ptr.as_ref() }) + } + } } #[stable(feature = "arc_weak", since = "1.4.0")] @@ -1126,10 +1134,10 @@ impl Clone for Weak { /// ``` #[inline] fn clone(&self) -> Weak { - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return Weak { ptr: self.ptr }; + let inner = if let Some(inner) = self.inner() { + inner } else { - unsafe { self.ptr.as_ref() } + return Weak { ptr: self.ptr }; }; // See comments in Arc::clone() for why this is relaxed. This can use a // fetch_add (ignoring the lock) because the weak count is only locked @@ -1204,10 +1212,10 @@ impl Drop for Weak { // weak count can only be locked if there was precisely one weak ref, // meaning that drop could only subsequently run ON that remaining weak // ref, which can only happen after the lock is released. - let inner = if self.ptr.as_ptr() as *const u8 as usize == WEAK_EMPTY { - return; + let inner = if let Some(inner) = self.inner() { + inner } else { - unsafe { self.ptr.as_ref() } + return }; if inner.weak.fetch_sub(1, Release) == 1 { -- cgit 1.4.1-3-g733a5 From 41730b7e2e2c28a13fe6d08a7ad47d31d68eccea Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Wed, 27 Jun 2018 18:09:21 +0200 Subject: Rc: remove unused allocation from Weak::new() Same as https://github.com/rust-lang/rust/pull/50357 --- src/liballoc/rc.rs | 59 ++++++++++++++++++++++++++++++++-------------------- src/liballoc/sync.rs | 2 +- 2 files changed, 37 insertions(+), 24 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 32d624e8fbc..3f32abe1ea9 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -253,7 +253,7 @@ use core::hash::{Hash, Hasher}; use core::intrinsics::abort; use core::marker; use core::marker::{Unsize, PhantomData}; -use core::mem::{self, align_of_val, forget, size_of_val, uninitialized}; +use core::mem::{self, align_of_val, forget, size_of_val}; use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; @@ -261,6 +261,7 @@ use core::convert::From; use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use string::String; +use sync::is_dangling; use vec::Vec; struct RcBox { @@ -1153,6 +1154,10 @@ impl From> for Rc<[T]> { /// [`None`]: ../../std/option/enum.Option.html#variant.None #[stable(feature = "rc_weak", since = "1.4.0")] pub struct Weak { + // This is a `NonNull` to allow optimizing the size of this type in enums, + // but it is not necessarily a valid pointer. + // `Weak::new` sets this to a dangling pointer so that it doesn’t need + // to allocate space on the heap. ptr: NonNull>, } @@ -1165,8 +1170,8 @@ impl !marker::Sync for Weak {} impl, U: ?Sized> CoerceUnsized> for Weak {} impl Weak { - /// Constructs a new `Weak`, allocating memory for `T` without initializing - /// it. Calling [`upgrade`] on the return value always gives [`None`]. + /// Constructs a new `Weak`, without allocating any memory. + /// Calling [`upgrade`] on the return value always gives [`None`]. /// /// [`upgrade`]: struct.Weak.html#method.upgrade /// [`None`]: ../../std/option/enum.Option.html @@ -1181,14 +1186,8 @@ impl Weak { /// ``` #[stable(feature = "downgraded_weak", since = "1.10.0")] pub fn new() -> Weak { - unsafe { - Weak { - ptr: Box::into_raw_non_null(box RcBox { - strong: Cell::new(0), - weak: Cell::new(1), - value: uninitialized(), - }), - } + Weak { + ptr: NonNull::dangling(), } } } @@ -1222,13 +1221,25 @@ impl Weak { /// ``` #[stable(feature = "rc_weak", since = "1.4.0")] pub fn upgrade(&self) -> Option> { - if self.strong() == 0 { + let inner = self.inner()?; + if inner.strong() == 0 { None } else { - self.inc_strong(); + inner.inc_strong(); Some(Rc { ptr: self.ptr, phantom: PhantomData }) } } + + /// Return `None` when the pointer is dangling and there is no allocated `RcBox`, + /// i.e. this `Weak` was created by `Weak::new` + #[inline] + fn inner(&self) -> Option<&RcBox> { + if is_dangling(self.ptr) { + None + } else { + Some(unsafe { self.ptr.as_ref() }) + } + } } #[stable(feature = "rc_weak", since = "1.4.0")] @@ -1258,12 +1269,14 @@ impl Drop for Weak { /// assert!(other_weak_foo.upgrade().is_none()); /// ``` fn drop(&mut self) { - unsafe { - self.dec_weak(); + if let Some(inner) = self.inner() { + inner.dec_weak(); // the weak count starts at 1, and will only go to zero if all // the strong pointers have disappeared. - if self.weak() == 0 { - Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + if inner.weak() == 0 { + unsafe { + Global.dealloc(self.ptr.cast(), Layout::for_value(self.ptr.as_ref())); + } } } } @@ -1284,7 +1297,9 @@ impl Clone for Weak { /// ``` #[inline] fn clone(&self) -> Weak { - self.inc_weak(); + if let Some(inner) = self.inner() { + inner.inc_weak() + } Weak { ptr: self.ptr } } } @@ -1317,7 +1332,7 @@ impl Default for Weak { } } -// NOTE: We checked_add here to deal with mem::forget safety. In particular +// NOTE: We checked_add here to deal with mem::forget safely. In particular // if you mem::forget Rcs (or Weaks), the ref-count can overflow, and then // you can free the allocation while outstanding Rcs (or Weaks) exist. // We abort because this is such a degenerate scenario that we don't care about @@ -1370,12 +1385,10 @@ impl RcBoxPtr for Rc { } } -impl RcBoxPtr for Weak { +impl RcBoxPtr for RcBox { #[inline(always)] fn inner(&self) -> &RcBox { - unsafe { - self.ptr.as_ref() - } + self } } diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index abc0befeb94..ea8616bf1d0 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -1038,7 +1038,7 @@ impl Weak { } } -fn is_dangling(ptr: NonNull) -> bool { +pub(crate) fn is_dangling(ptr: NonNull) -> bool { let address = ptr.as_ptr() as *mut () as usize; let align = align_of_val(unsafe { ptr.as_ref() }); address == align -- cgit 1.4.1-3-g733a5 From 5717d99d1b3b76ec7814c95dfcc0399ab4ddaa83 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Fri, 6 Jul 2018 19:30:09 +0200 Subject: Add some unit tests for dangling Weak references --- src/liballoc/tests/arc.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++ src/liballoc/tests/lib.rs | 2 ++ src/liballoc/tests/rc.rs | 55 +++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 112 insertions(+) create mode 100644 src/liballoc/tests/arc.rs create mode 100644 src/liballoc/tests/rc.rs (limited to 'src/liballoc') diff --git a/src/liballoc/tests/arc.rs b/src/liballoc/tests/arc.rs new file mode 100644 index 00000000000..753873dd294 --- /dev/null +++ b/src/liballoc/tests/arc.rs @@ -0,0 +1,55 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::any::Any; +use std::sync::{Arc, Weak}; + +#[test] +fn uninhabited() { + enum Void {} + let mut a = Weak::::new(); + a = a.clone(); + assert!(a.upgrade().is_none()); + + let mut a: Weak = a; // Unsizing + a = a.clone(); + assert!(a.upgrade().is_none()); +} + +#[test] +fn slice() { + let a: Arc<[u32; 3]> = Arc::new([3, 2, 1]); + let a: Arc<[u32]> = a; // Unsizing + let b: Arc<[u32]> = Arc::from(&[3, 2, 1][..]); // Conversion + assert_eq!(a, b); + + // Exercise is_dangling() with a DST + let mut a = Arc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); +} + +#[test] +fn trait_object() { + let a: Arc = Arc::new(4); + let a: Arc = a; // Unsizing + + // Exercise is_dangling() with a DST + let mut a = Arc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); + + let mut b = Weak::::new(); + b = b.clone(); + assert!(b.upgrade().is_none()); + let mut b: Weak = b; // Unsizing + b = b.clone(); + assert!(b.upgrade().is_none()); +} diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index dbac2c0bb18..2c361598e89 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -32,12 +32,14 @@ extern crate rand; use std::hash::{Hash, Hasher}; use std::collections::hash_map::DefaultHasher; +mod arc; mod binary_heap; mod btree; mod cow_str; mod fmt; mod heap; mod linked_list; +mod rc; mod slice; mod str; mod string; diff --git a/src/liballoc/tests/rc.rs b/src/liballoc/tests/rc.rs new file mode 100644 index 00000000000..baa0406acfc --- /dev/null +++ b/src/liballoc/tests/rc.rs @@ -0,0 +1,55 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::any::Any; +use std::rc::{Rc, Weak}; + +#[test] +fn uninhabited() { + enum Void {} + let mut a = Weak::::new(); + a = a.clone(); + assert!(a.upgrade().is_none()); + + let mut a: Weak = a; // Unsizing + a = a.clone(); + assert!(a.upgrade().is_none()); +} + +#[test] +fn slice() { + let a: Rc<[u32; 3]> = Rc::new([3, 2, 1]); + let a: Rc<[u32]> = a; // Unsizing + let b: Rc<[u32]> = Rc::from(&[3, 2, 1][..]); // Conversion + assert_eq!(a, b); + + // Exercise is_dangling() with a DST + let mut a = Rc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); +} + +#[test] +fn trait_object() { + let a: Rc = Rc::new(4); + let a: Rc = a; // Unsizing + + // Exercise is_dangling() with a DST + let mut a = Rc::downgrade(&a); + a = a.clone(); + assert!(a.upgrade().is_some()); + + let mut b = Weak::::new(); + b = b.clone(); + assert!(b.upgrade().is_none()); + let mut b: Weak = b; // Unsizing + b = b.clone(); + assert!(b.upgrade().is_none()); +} -- cgit 1.4.1-3-g733a5 From 67202b8b68d93c90e89ca001945865f13bc97154 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Jul 2018 01:44:57 +0200 Subject: Fix is_dangling import when Arc is #[cfg]’ed out MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/liballoc/rc.rs | 7 ++++++- src/liballoc/sync.rs | 7 +------ 2 files changed, 7 insertions(+), 7 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 3f32abe1ea9..5b71d0b85a0 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -261,7 +261,6 @@ use core::convert::From; use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use string::String; -use sync::is_dangling; use vec::Vec; struct RcBox { @@ -1192,6 +1191,12 @@ impl Weak { } } +pub(crate) fn is_dangling(ptr: NonNull) -> bool { + let address = ptr.as_ptr() as *mut () as usize; + let align = align_of_val(unsafe { ptr.as_ref() }); + address == align +} + impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Rc`], extending /// the lifetime of the value if successful. diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index ea8616bf1d0..6710878b31d 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -34,6 +34,7 @@ use core::convert::From; use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use boxed::Box; +use rc::is_dangling; use string::String; use vec::Vec; @@ -1038,12 +1039,6 @@ impl Weak { } } -pub(crate) fn is_dangling(ptr: NonNull) -> bool { - let address = ptr.as_ptr() as *mut () as usize; - let align = align_of_val(unsafe { ptr.as_ref() }); - address == align -} - impl Weak { /// Attempts to upgrade the `Weak` pointer to an [`Arc`], extending /// the lifetime of the value if successful. -- cgit 1.4.1-3-g733a5 From b842177cfac47668376c98932a931eaf47eed542 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Sat, 7 Jul 2018 00:43:11 +0200 Subject: Add the `alloc::prelude` module It contains the re-exports that are in `std::prelude::v1` but not in `core::prelude::v1`. Calling it prelude is somewhat of a misnomer since (unlike those modules in `std` or `core`) its contents are never implicitly imported in modules. Rather it is intended to be used with an explicit glob import like `use alloc::prelude::*;`. However there is precedent for the same misnomer with `std::io::prelude`, for example. This new module is unstable with the same feature name as the `alloc` care. They are proposed for stabilization together in RFC https://github.com/rust-lang/rfcs/pull/2480 --- src/liballoc/lib.rs | 2 +- src/liballoc/prelude.rs | 29 +++++++++++++++++++++++++++++ 2 files changed, 30 insertions(+), 1 deletion(-) create mode 100644 src/liballoc/prelude.rs (limited to 'src/liballoc') diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 35bf8d1b792..ef619527e06 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -169,7 +169,7 @@ pub mod collections; pub mod sync; pub mod rc; pub mod raw_vec; - +pub mod prelude; pub mod borrow; pub mod fmt; pub mod slice; diff --git a/src/liballoc/prelude.rs b/src/liballoc/prelude.rs new file mode 100644 index 00000000000..53b5e93a66e --- /dev/null +++ b/src/liballoc/prelude.rs @@ -0,0 +1,29 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +//! The alloc Prelude +//! +//! The purpose of this module is to alleviate imports of commonly-used +//! items of the `alloc` crate by adding a glob import to the top of modules: +//! +//! ``` +//! # #![allow(unused_imports)] +//! # #![feature(alloc)] +//! extern crate alloc; +//! use alloc::prelude::*; +//! ``` + +#![unstable(feature = "alloc", issue = "27783")] + +#[unstable(feature = "alloc", issue = "27783")] pub use borrow::ToOwned; +#[unstable(feature = "alloc", issue = "27783")] pub use boxed::Box; +#[unstable(feature = "alloc", issue = "27783")] pub use slice::SliceConcatExt; +#[unstable(feature = "alloc", issue = "27783")] pub use string::{String, ToString}; +#[unstable(feature = "alloc", issue = "27783")] pub use vec::Vec; -- cgit 1.4.1-3-g733a5 From 295768ae8fd07ec129142668bdd8030f9c3856ac Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Mon, 9 Jul 2018 05:01:39 +0200 Subject: Performance improvement of Vec's swap_remove. --- src/liballoc/vec.rs | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index fbbaced540e..36be21de33a 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -809,9 +809,13 @@ impl Vec { #[inline] #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { - let length = self.len(); - self.swap(index, length - 1); - self.pop().unwrap() + unsafe { + // We replace self[index] with the last element. Note that this is + // safe even when index == self.len() - 1, as pop() only uses + // ptr::read and leaves the memory at self[index] untouched. + let hole: *mut T = &mut self[index]; + ptr::replace(hole, self.pop().unwrap()) + } } /// Inserts an element at position `index` within the vector, shifting all -- cgit 1.4.1-3-g733a5 From 6faa295cecc7940526dc467e39ce20e652851cfb Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Mon, 9 Jul 2018 06:13:58 +0200 Subject: Reimplemented Vec's swap_remove to not rely on pop. --- src/liballoc/vec.rs | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 36be21de33a..98660fe415d 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -810,11 +810,13 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { unsafe { - // We replace self[index] with the last element. Note that this is - // safe even when index == self.len() - 1, as pop() only uses - // ptr::read and leaves the memory at self[index] untouched. + // We replace self[index] with the last element. Note that if the + // bounds check on hole succeeds there must be a last element (which + // can be self[index] itself). let hole: *mut T = &mut self[index]; - ptr::replace(hole, self.pop().unwrap()) + let last = ptr::read(self.get_unchecked(self.len - 1)); + self.len -= 1; + ptr::replace(hole, last) } } -- cgit 1.4.1-3-g733a5 From e529dfd590e6c7e0c19bbf0f3c72e34f25c0cfb8 Mon Sep 17 00:00:00 2001 From: Orson Peters Date: Mon, 9 Jul 2018 06:31:24 +0200 Subject: Removed a single trailing space. Oops. --- src/liballoc/vec.rs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/vec.rs b/src/liballoc/vec.rs index 98660fe415d..5efe1e23309 100644 --- a/src/liballoc/vec.rs +++ b/src/liballoc/vec.rs @@ -810,7 +810,7 @@ impl Vec { #[stable(feature = "rust1", since = "1.0.0")] pub fn swap_remove(&mut self, index: usize) -> T { unsafe { - // We replace self[index] with the last element. Note that if the + // We replace self[index] with the last element. Note that if the // bounds check on hole succeeds there must be a last element (which // can be self[index] itself). let hole: *mut T = &mut self[index]; -- cgit 1.4.1-3-g733a5 From 6aeeda714e04ac34205a7b948e5585e00d0fa14d Mon Sep 17 00:00:00 2001 From: Josh Stone Date: Mon, 9 Jul 2018 13:25:36 -0700 Subject: doc: Clarify the lifetime returned by `Box::leak` `Box::leak` mentions that it can return a `'static` reference, but it wasn't immediately clear to me why it doesn't always do so. This is because of the `T: 'a` constraint needed to form a valid reference, and in general we want to be more flexible than requiring `T: 'static`. This patch tries to clarify the relationship between `T` and `'a`. --- src/liballoc/boxed.rs | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fb16bdf0ab4..8a65843b50d 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -194,7 +194,9 @@ impl Box { } /// Consumes and leaks the `Box`, returning a mutable reference, - /// `&'a mut T`. Here, the lifetime `'a` may be chosen to be `'static`. + /// `&'a mut T`. Note that the type `T` must outlive the chosen lifetime + /// `'a`. If the type has only static references, or none at all, then this + /// may be chosen to be `'static`. /// /// This function is mainly useful for data that lives for the remainder of /// the program's life. Dropping the returned reference will cause a memory -- cgit 1.4.1-3-g733a5 From 296e72f11c4120c7b38a0cc580ef8990e7a1c36d Mon Sep 17 00:00:00 2001 From: ljedrz Date: Tue, 10 Jul 2018 20:45:16 +0200 Subject: Deny bare trait objects in in src/liballoc --- src/liballoc/boxed.rs | 18 +++++++++--------- src/liballoc/lib.rs | 1 + src/liballoc/rc.rs | 4 ++-- src/liballoc/sync.rs | 2 +- 4 files changed, 13 insertions(+), 12 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fb16bdf0ab4..44f15981137 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -446,7 +446,7 @@ impl From> for Box<[u8]> { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -468,10 +468,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { + pub fn downcast(self) -> Result, Box> { if self.is::() { unsafe { - let raw: *mut Any = Box::into_raw(self); + let raw: *mut dyn Any = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T)) } } else { @@ -480,7 +480,7 @@ impl Box { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -502,10 +502,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { - >::downcast(self).map_err(|s| unsafe { + pub fn downcast(self) -> Result, Box> { + >::downcast(self).map_err(|s| unsafe { // reapply the Send marker - Box::from_raw(Box::into_raw(s) as *mut (Any + Send)) + Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) }) } } @@ -643,7 +643,7 @@ impl FnBox for F #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + 'a> { +impl<'a, A, R> FnOnce for Box + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { @@ -653,7 +653,7 @@ impl<'a, A, R> FnOnce for Box + 'a> { #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + Send + 'a> { +impl<'a, A, R> FnOnce for Box + Send + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ef619527e06..63cf01a0fac 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -72,6 +72,7 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] #![needs_allocator] +#![deny(bare_trait_objects)] #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index f7c12b98f48..d55f6575a37 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -618,7 +618,7 @@ impl Rc { } } -impl Rc { +impl Rc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Rc` to a concrete type. @@ -641,7 +641,7 @@ impl Rc { /// print_if_string(Rc::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Rc> { + pub fn downcast(self) -> Result, Rc> { if (*self).is::() { let ptr = self.ptr.cast::>(); forget(self); diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 5a738fc5444..920678bbd70 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -978,7 +978,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { } } -impl Arc { +impl Arc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Arc` to a concrete type. -- cgit 1.4.1-3-g733a5 From cd44b3ddaddf22ef3c3b00f31491c660b851f8ee Mon Sep 17 00:00:00 2001 From: ljedrz Date: Tue, 10 Jul 2018 22:32:19 +0200 Subject: Add missing dyn in liballoc --- src/liballoc/boxed.rs | 18 +++++++++--------- src/liballoc/boxed_test.rs | 20 ++++++++++---------- src/liballoc/lib.rs | 1 - src/liballoc/rc.rs | 18 +++++++++--------- src/liballoc/sync.rs | 16 ++++++++-------- 5 files changed, 36 insertions(+), 37 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 44f15981137..fb16bdf0ab4 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -446,7 +446,7 @@ impl From> for Box<[u8]> { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -468,10 +468,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { + pub fn downcast(self) -> Result, Box> { if self.is::() { unsafe { - let raw: *mut dyn Any = Box::into_raw(self); + let raw: *mut Any = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T)) } } else { @@ -480,7 +480,7 @@ impl Box { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -502,10 +502,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { - >::downcast(self).map_err(|s| unsafe { + pub fn downcast(self) -> Result, Box> { + >::downcast(self).map_err(|s| unsafe { // reapply the Send marker - Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) + Box::from_raw(Box::into_raw(s) as *mut (Any + Send)) }) } } @@ -643,7 +643,7 @@ impl FnBox for F #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + 'a> { +impl<'a, A, R> FnOnce for Box + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { @@ -653,7 +653,7 @@ impl<'a, A, R> FnOnce for Box + 'a> { #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + Send + 'a> { +impl<'a, A, R> FnOnce for Box + Send + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { diff --git a/src/liballoc/boxed_test.rs b/src/liballoc/boxed_test.rs index 837f8dfaca1..55995742a4a 100644 --- a/src/liballoc/boxed_test.rs +++ b/src/liballoc/boxed_test.rs @@ -31,8 +31,8 @@ struct Test; #[test] fn any_move() { - let a = Box::new(8) as Box; - let b = Box::new(Test) as Box; + let a = Box::new(8) as Box; + let b = Box::new(Test) as Box; match a.downcast::() { Ok(a) => { @@ -47,8 +47,8 @@ fn any_move() { Err(..) => panic!(), } - let a = Box::new(8) as Box; - let b = Box::new(Test) as Box; + let a = Box::new(8) as Box; + let b = Box::new(Test) as Box; assert!(a.downcast::>().is_err()); assert!(b.downcast::>().is_err()); @@ -56,8 +56,8 @@ fn any_move() { #[test] fn test_show() { - let a = Box::new(8) as Box; - let b = Box::new(Test) as Box; + let a = Box::new(8) as Box; + let b = Box::new(Test) as Box; let a_str = format!("{:?}", a); let b_str = format!("{:?}", b); assert_eq!(a_str, "Any"); @@ -65,8 +65,8 @@ fn test_show() { static EIGHT: usize = 8; static TEST: Test = Test; - let a = &EIGHT as &Any; - let b = &TEST as &Any; + let a = &EIGHT as &dyn Any; + let b = &TEST as &dyn Any; let s = format!("{:?}", a); assert_eq!(s, "Any"); let s = format!("{:?}", b); @@ -110,12 +110,12 @@ fn raw_trait() { } } - let x: Box = Box::new(Bar(17)); + let x: Box = Box::new(Bar(17)); let p = Box::into_raw(x); unsafe { assert_eq!(17, (*p).get()); (*p).set(19); - let y: Box = Box::from_raw(p); + let y: Box = Box::from_raw(p); assert_eq!(19, y.get()); } } diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index 63cf01a0fac..ef619527e06 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -72,7 +72,6 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] #![needs_allocator] -#![deny(bare_trait_objects)] #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index d55f6575a37..3643f78d323 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -618,7 +618,7 @@ impl Rc { } } -impl Rc { +impl Rc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Rc` to a concrete type. @@ -641,7 +641,7 @@ impl Rc { /// print_if_string(Rc::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Rc> { + pub fn downcast(self) -> Result, Rc> { if (*self).is::() { let ptr = self.ptr.cast::>(); forget(self); @@ -1554,7 +1554,7 @@ mod tests { assert_eq!(unsafe { &*ptr }, "foo"); assert_eq!(rc, rc2); - let rc: Rc = Rc::new(123); + let rc: Rc = Rc::new(123); let ptr = Rc::into_raw(rc.clone()); let rc2 = unsafe { Rc::from_raw(ptr) }; @@ -1755,8 +1755,8 @@ mod tests { use std::fmt::Display; use std::string::ToString; - let b: Box = box 123; - let r: Rc = Rc::from(b); + let b: Box = box 123; + let r: Rc = Rc::from(b); assert_eq!(r.to_string(), "123"); } @@ -1765,8 +1765,8 @@ mod tests { fn test_from_box_trait_zero_sized() { use std::fmt::Debug; - let b: Box = box (); - let r: Rc = Rc::from(b); + let b: Box = box (); + let r: Rc = Rc::from(b); assert_eq!(format!("{:?}", r), "()"); } @@ -1783,8 +1783,8 @@ mod tests { fn test_downcast() { use std::any::Any; - let r1: Rc = Rc::new(i32::max_value()); - let r2: Rc = Rc::new("abc"); + let r1: Rc = Rc::new(i32::max_value()); + let r2: Rc = Rc::new("abc"); assert!(r1.clone().downcast::().is_err()); diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 920678bbd70..35aae191683 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -978,7 +978,7 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { } } -impl Arc { +impl Arc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Arc` to a concrete type. @@ -1574,7 +1574,7 @@ mod tests { assert_eq!(unsafe { &*ptr }, "foo"); assert_eq!(arc, arc2); - let arc: Arc = Arc::new(123); + let arc: Arc = Arc::new(123); let ptr = Arc::into_raw(arc.clone()); let arc2 = unsafe { Arc::from_raw(ptr) }; @@ -1879,8 +1879,8 @@ mod tests { use std::fmt::Display; use std::string::ToString; - let b: Box = box 123; - let r: Arc = Arc::from(b); + let b: Box = box 123; + let r: Arc = Arc::from(b); assert_eq!(r.to_string(), "123"); } @@ -1889,8 +1889,8 @@ mod tests { fn test_from_box_trait_zero_sized() { use std::fmt::Debug; - let b: Box = box (); - let r: Arc = Arc::from(b); + let b: Box = box (); + let r: Arc = Arc::from(b); assert_eq!(format!("{:?}", r), "()"); } @@ -1907,8 +1907,8 @@ mod tests { fn test_downcast() { use std::any::Any; - let r1: Arc = Arc::new(i32::max_value()); - let r2: Arc = Arc::new("abc"); + let r1: Arc = Arc::new(i32::max_value()); + let r2: Arc = Arc::new("abc"); assert!(r1.clone().downcast::().is_err()); -- cgit 1.4.1-3-g733a5 From 217f8fbd4512ebe94fb021ee564ea6c6dae6a919 Mon Sep 17 00:00:00 2001 From: ljedrz Date: Wed, 11 Jul 2018 10:19:54 +0200 Subject: Revert borked changes in last commit. --- src/liballoc/boxed.rs | 18 +++++++++--------- src/liballoc/lib.rs | 1 + src/liballoc/rc.rs | 4 ++-- src/liballoc/sync.rs | 6 +++--- 4 files changed, 15 insertions(+), 14 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index fb16bdf0ab4..44f15981137 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -446,7 +446,7 @@ impl From> for Box<[u8]> { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -468,10 +468,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { + pub fn downcast(self) -> Result, Box> { if self.is::() { unsafe { - let raw: *mut Any = Box::into_raw(self); + let raw: *mut dyn Any = Box::into_raw(self); Ok(Box::from_raw(raw as *mut T)) } } else { @@ -480,7 +480,7 @@ impl Box { } } -impl Box { +impl Box { #[inline] #[stable(feature = "rust1", since = "1.0.0")] /// Attempt to downcast the box to a concrete type. @@ -502,10 +502,10 @@ impl Box { /// print_if_string(Box::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Box> { - >::downcast(self).map_err(|s| unsafe { + pub fn downcast(self) -> Result, Box> { + >::downcast(self).map_err(|s| unsafe { // reapply the Send marker - Box::from_raw(Box::into_raw(s) as *mut (Any + Send)) + Box::from_raw(Box::into_raw(s) as *mut (dyn Any + Send)) }) } } @@ -643,7 +643,7 @@ impl FnBox for F #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + 'a> { +impl<'a, A, R> FnOnce for Box + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { @@ -653,7 +653,7 @@ impl<'a, A, R> FnOnce for Box + 'a> { #[unstable(feature = "fnbox", reason = "will be deprecated if and when `Box` becomes usable", issue = "28796")] -impl<'a, A, R> FnOnce for Box + Send + 'a> { +impl<'a, A, R> FnOnce for Box + Send + 'a> { type Output = R; extern "rust-call" fn call_once(self, args: A) -> R { diff --git a/src/liballoc/lib.rs b/src/liballoc/lib.rs index ef619527e06..63cf01a0fac 100644 --- a/src/liballoc/lib.rs +++ b/src/liballoc/lib.rs @@ -72,6 +72,7 @@ test(no_crate_inject, attr(allow(unused_variables), deny(warnings))))] #![no_std] #![needs_allocator] +#![deny(bare_trait_objects)] #![deny(missing_debug_implementations)] #![cfg_attr(test, allow(deprecated))] // rand diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index 3643f78d323..d76acb28df9 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -618,7 +618,7 @@ impl Rc { } } -impl Rc { +impl Rc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] /// Attempt to downcast the `Rc` to a concrete type. @@ -641,7 +641,7 @@ impl Rc { /// print_if_string(Rc::new(0i8)); /// } /// ``` - pub fn downcast(self) -> Result, Rc> { + pub fn downcast(self) -> Result, Rc> { if (*self).is::() { let ptr = self.ptr.cast::>(); forget(self); diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 35aae191683..5def0237e7e 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -978,10 +978,10 @@ unsafe impl<#[may_dangle] T: ?Sized> Drop for Arc { } } -impl Arc { +impl Arc { #[inline] #[stable(feature = "rc_downcast", since = "1.29.0")] - /// Attempt to downcast the `Arc` to a concrete type. + /// Attempt to downcast the `Arc` to a concrete type. /// /// # Examples /// @@ -989,7 +989,7 @@ impl Arc { /// use std::any::Any; /// use std::sync::Arc; /// - /// fn print_if_string(value: Arc) { + /// fn print_if_string(value: Arc) { /// if let Ok(string) = value.downcast::() { /// println!("String ({}): {}", string.len(), string); /// } -- cgit 1.4.1-3-g733a5 From 24bc854b8c95ccf8e229d3982466b71ae778d04e Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Thu, 19 Jul 2018 19:58:06 +0200 Subject: Non-naive implementation for `VecDeque.append` --- src/liballoc/collections/vec_deque.rs | 140 +++++++++++++++++++++++++++++++++- src/liballoc/tests/vec_deque.rs | 54 +++++++++++++ 2 files changed, 192 insertions(+), 2 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index ba92b886138..d0b70b5db2d 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -1834,8 +1834,144 @@ impl VecDeque { #[inline] #[stable(feature = "append", since = "1.4.0")] pub fn append(&mut self, other: &mut Self) { - // naive impl - self.extend(other.drain(..)); + // Copy from src[i1..i1 + len] to dst[i2..i2 + len]. + // Does not check if the ranges are valid. + unsafe fn copy_part(i1: usize, i2: usize, len: usize, src: &[T], dst: &mut [T]) { + debug_assert!(src.get(i1..i1 + len).is_some() && dst.get(i2..i2 + len).is_some()); + ptr::copy_nonoverlapping(src.as_ptr().add(i1), dst.as_mut_ptr().add(i2), len); + } + + let src_total = other.len(); + + // Guarantees there is space in `self` for `other`. + self.reserve(src_total); + + self.head = { + let dst_start_1 = self.head; + let src_start_1 = other.tail; + let dst_wrap_point = self.cap(); + let src_wrap_point = other.cap(); + + let dst = unsafe { self.buffer_as_mut_slice() }; + let src = unsafe { other.buffer_as_slice() }; + + let src_wraps = other.tail > other.head; + let dst_wraps = dst_start_1 + src_total > dst_wrap_point; + + // When minimizing the amount of calls to `copy_part`, there are + // 6 different cases to handle. Whether src and/or dst wrap are 4 + // combinations and there are 3 distinct cases when they both wrap. + // 6 = 3 + 1 + 1 + 1 + match (src_wraps, dst_wraps) { + (true, true) => { + let dst_before_wrap = dst_wrap_point - dst_start_1; + let src_before_wrap = src_wrap_point - src_start_1; + + if src_before_wrap < dst_before_wrap { + // src + // [o o o . . . . . . o o o] + // 2 3 3 1 1 1 + // + // dst + // [. . . . . . o o . . . .] + // 3 3 H 1 1 1 2 + let src_2 = dst_before_wrap - src_before_wrap; + let dst_start_2 = dst_start_1 + src_before_wrap; + let src_3 = src_total - dst_before_wrap; + + unsafe { + copy_part(src_start_1, dst_start_1, src_before_wrap, src, dst); + copy_part(0, dst_start_2, src_2, src, dst); + copy_part(src_2, 0, src_3, src, dst); + } + src_3 + } else if src_before_wrap > dst_before_wrap { + // src + // [o o o . . . . . o o o o] + // 3 3 3 1 1 2 2 + // + // dst + // [. . . . . . o o o o . .] + // 2 2 3 3 3 H 1 1 + let src_2 = src_before_wrap - dst_before_wrap; + let src_start_2 = src_start_1 + dst_before_wrap; + let src_3 = src_total - src_before_wrap; + + unsafe { + copy_part(src_start_1, dst_start_1, dst_before_wrap, src, dst); + copy_part(src_start_2, 0, src_2, src, dst); + copy_part(0, src_2, src_3, src, dst); + } + src_2 + src_3 + } else { + // src + // [o o . . . . . . . o o o] + // 2 2 1 1 1 + // + // dst + // [. . . . . . . o o . . .] + // 2 2 H 1 1 1 + let src_2 = src_total - src_before_wrap; + + unsafe { + copy_part(src_start_1, dst_start_1, src_before_wrap, src, dst); + copy_part(0, 0, src_2, src, dst); + } + src_2 + } + } + (false, true) => { + // src + // [. . . o o o o o . . . .] + // 1 1 2 2 2 + // + // dst + // [. . . . . . . o o o . .] + // 2 2 2 H 1 1 + let dst_1 = dst_wrap_point - dst_start_1; + let src_start_2 = src_start_1 + dst_1; + let dst_2 = src_total - dst_1; + + unsafe { + copy_part(src_start_1, dst_start_1, dst_1, src, dst); + copy_part(src_start_2, 0, dst_2, src, dst); + } + dst_2 + } + (true, false) => { + // src + // [o o . . . . . . . o o o] + // 2 2 1 1 1 + // + // dst + // [. o o . . . . . . . . .] + // 1 1 1 2 2 H + let src_1 = src_wrap_point - src_start_1; + let dst_start_2 = dst_start_1 + src_1; + let src_2 = src_total - src_1; + + unsafe { + copy_part(src_start_1, dst_start_1, src_1, src, dst); + copy_part(0, dst_start_2, src_2, src, dst); + } + dst_start_1 + src_1 + src_2 + } + (false, false) => { + // src + // [. . . o o o . . . . . .] + // 1 1 1 + // + // dst + // [. o o o o o . . . . . .] + // 1 1 1 H + unsafe { + copy_part(src_start_1, dst_start_1, src_total, src, dst); + } + dst_start_1 + src_total + } + } + }; + other.clear(); } /// Retains only the elements specified by the predicate. diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index 4d55584e2f4..0c8c1f2c65b 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -928,6 +928,60 @@ fn test_append() { assert_eq!(a.iter().cloned().collect::>(), []); } +#[test] +fn test_append_advanced() { + fn check( + a_push_back: usize, + a_pop_back: usize, + b_push_back: usize, + b_pop_back: usize, + a_push_front: usize, + a_pop_front: usize, + b_push_front: usize, + b_pop_front: usize + ) { + let mut taken = 0; + let mut a = VecDeque::new(); + let mut b = VecDeque::new(); + for n in (taken..).take(a_push_back) { + a.push_back(n); + } + taken += a_push_back; + for n in (taken..).take(a_push_front) { + a.push_front(n); + } + taken += a_push_front; + for n in (taken..).take(b_push_back) { + b.push_back(n); + } + taken += b_push_back; + for n in (taken..).take(b_push_front) { + b.push_front(n); + } + + a.drain(..a_pop_back); + a.drain(a_pop_front..); + b.drain(..b_pop_back); + b.drain(b_pop_front..); + let checked = a.iter().chain(b.iter()).map(|&x| x).collect::>(); + a.append(&mut b); + assert_eq!(a, checked); + assert!(b.is_empty()); + } + for a_push in 0..17 { + for a_pop in 0..a_push { + for b_push in 0..17 { + for b_pop in 0..b_push { + check(a_push, a_pop, b_push, b_pop, 0, 0, 0, 0); + check(a_push, a_pop, b_push, b_pop, a_push, 0, 0, 0); + check(a_push, a_pop, b_push, b_pop, 0, 0, b_push, 0); + check(0, 0, 0, 0, a_push, a_pop, b_push, b_pop); + } + } + } + } +} + #[test] fn test_retain() { let mut buf = VecDeque::new(); -- cgit 1.4.1-3-g733a5 From 2eb83ee527fdb4bcd740ea41c43e9d2d52f99c1c Mon Sep 17 00:00:00 2001 From: Vadim Petrochenkov Date: Wed, 18 Jul 2018 21:53:54 +0300 Subject: data_structures: Add a reference wrapper for pointer-indexed maps/sets Use `ptr::eq` for comparing pointers --- src/liballoc/collections/btree/node.rs | 2 +- src/librustc/lint/mod.rs | 4 +-- src/librustc/ty/mod.rs | 9 +++---- src/librustc/ty/query/job.rs | 9 ++++--- src/librustc_data_structures/lib.rs | 21 +++++++-------- src/librustc_data_structures/ptr_key.rs | 45 +++++++++++++++++++++++++++++++++ src/librustc_resolve/resolve_imports.rs | 16 ++++++------ 7 files changed, 76 insertions(+), 30 deletions(-) create mode 100644 src/librustc_data_structures/ptr_key.rs (limited to 'src/liballoc') diff --git a/src/liballoc/collections/btree/node.rs b/src/liballoc/collections/btree/node.rs index 19bdcbc6ad6..0ae45b31232 100644 --- a/src/liballoc/collections/btree/node.rs +++ b/src/liballoc/collections/btree/node.rs @@ -103,7 +103,7 @@ impl LeafNode { } fn is_shared_root(&self) -> bool { - self as *const _ == &EMPTY_ROOT_NODE as *const _ as *const LeafNode + ptr::eq(self, &EMPTY_ROOT_NODE as *const _ as *const _) } } diff --git a/src/librustc/lint/mod.rs b/src/librustc/lint/mod.rs index e3d35a7c105..dc1c9f7c108 100644 --- a/src/librustc/lint/mod.rs +++ b/src/librustc/lint/mod.rs @@ -39,7 +39,7 @@ use hir::intravisit; use hir; use lint::builtin::BuiltinLintDiagnostics; use session::{Session, DiagnosticMessageId}; -use std::hash; +use std::{hash, ptr}; use syntax::ast; use syntax::codemap::MultiSpan; use syntax::edition::Edition; @@ -354,7 +354,7 @@ pub struct LintId { impl PartialEq for LintId { fn eq(&self, other: &LintId) -> bool { - (self.lint as *const Lint) == (other.lint as *const Lint) + ptr::eq(self.lint, other.lint) } } diff --git a/src/librustc/ty/mod.rs b/src/librustc/ty/mod.rs index 178f0d3cdcb..bd24b93f029 100644 --- a/src/librustc/ty/mod.rs +++ b/src/librustc/ty/mod.rs @@ -47,7 +47,7 @@ use std::ops::Deref; use rustc_data_structures::sync::{self, Lrc, ParallelIterator, par_iter}; use std::slice; use std::vec::IntoIter; -use std::mem; +use std::{mem, ptr}; use syntax::ast::{self, DUMMY_NODE_ID, Name, Ident, NodeId}; use syntax::attr; use syntax::ext::hygiene::Mark; @@ -527,8 +527,7 @@ impl<'tcx> PartialOrd for TyS<'tcx> { impl<'tcx> PartialEq for TyS<'tcx> { #[inline] fn eq(&self, other: &TyS<'tcx>) -> bool { - // (self as *const _) == (other as *const _) - (self as *const TyS<'tcx>) == (other as *const TyS<'tcx>) + ptr::eq(self, other) } } impl<'tcx> Eq for TyS<'tcx> {} @@ -678,7 +677,7 @@ impl PartialOrd for Slice where T: PartialOrd { impl PartialEq for Slice { #[inline] fn eq(&self, other: &Slice) -> bool { - (self as *const _) == (other as *const _) + ptr::eq(self, other) } } impl Eq for Slice {} @@ -1730,7 +1729,7 @@ impl Ord for AdtDef { impl PartialEq for AdtDef { // AdtDef are always interned and this is part of TyS equality #[inline] - fn eq(&self, other: &Self) -> bool { self as *const _ == other as *const _ } + fn eq(&self, other: &Self) -> bool { ptr::eq(self, other) } } impl Eq for AdtDef {} diff --git a/src/librustc/ty/query/job.rs b/src/librustc/ty/query/job.rs index a54deeca293..6cc71642c42 100644 --- a/src/librustc/ty/query/job.rs +++ b/src/librustc/ty/query/job.rs @@ -11,6 +11,7 @@ #![allow(warnings)] use std::mem; +use rustc_data_structures::ptr_key::PtrKey; use rustc_data_structures::sync::{Lock, LockGuard, Lrc, Weak}; use rustc_data_structures::OnDrop; use syntax_pos::Span; @@ -20,7 +21,7 @@ use ty::query::plumbing::CycleError; use ty::context::TyCtxt; use errors::Diagnostic; use std::process; -use std::fmt; +use std::{fmt, ptr}; use std::collections::HashSet; #[cfg(parallel_queries)] use { @@ -124,7 +125,7 @@ impl<'tcx> QueryJob<'tcx> { while let Some(job) = current_job { cycle.insert(0, job.info.clone()); - if &*job as *const _ == self as *const _ { + if ptr::eq(&*job, self) { // This is the end of the cycle // The span entry we included was for the usage // of the cycle itself, and not part of the cycle @@ -282,7 +283,7 @@ where fn cycle_check<'tcx>(query: Lrc>, span: Span, stack: &mut Vec<(Span, Lrc>)>, - visited: &mut HashSet<*const QueryJob<'tcx>> + visited: &mut HashSet>> ) -> Option>> { if visited.contains(&query.as_ptr()) { return if let Some(p) = stack.iter().position(|q| q.1.as_ptr() == query.as_ptr()) { @@ -321,7 +322,7 @@ fn cycle_check<'tcx>(query: Lrc>, #[cfg(parallel_queries)] fn connected_to_root<'tcx>( query: Lrc>, - visited: &mut HashSet<*const QueryJob<'tcx>> + visited: &mut HashSet>> ) -> bool { // We already visited this or we're deliberately ignoring it if visited.contains(&query.as_ptr()) { diff --git a/src/librustc_data_structures/lib.rs b/src/librustc_data_structures/lib.rs index b386f887d77..ef0d57c7b7c 100644 --- a/src/librustc_data_structures/lib.rs +++ b/src/librustc_data_structures/lib.rs @@ -56,29 +56,30 @@ extern crate rustc_cratesio_shim; pub use rustc_serialize::hex::ToHex; -pub mod array_vec; pub mod accumulate_vec; -pub mod small_vec; +pub mod array_vec; pub mod base_n; pub mod bitslice; pub mod bitvec; +pub mod flock; +pub mod fx; +pub mod graph; pub mod indexed_set; pub mod indexed_vec; pub mod obligation_forest; +pub mod owning_ref; +pub mod ptr_key; pub mod sip128; +pub mod small_vec; pub mod snapshot_map; pub use ena::snapshot_vec; +pub mod sorted_map; pub mod stable_hasher; -pub mod transitive_relation; -pub use ena::unify; -pub mod fx; -pub mod tuple_slice; -pub mod graph; -pub mod flock; pub mod sync; -pub mod owning_ref; pub mod tiny_list; -pub mod sorted_map; +pub mod transitive_relation; +pub mod tuple_slice; +pub use ena::unify; pub mod work_queue; pub struct OnDrop(pub F); diff --git a/src/librustc_data_structures/ptr_key.rs b/src/librustc_data_structures/ptr_key.rs new file mode 100644 index 00000000000..6835dab38df --- /dev/null +++ b/src/librustc_data_structures/ptr_key.rs @@ -0,0 +1,45 @@ +// Copyright 2018 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 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::{hash, ptr}; +use std::ops::Deref; + +/// A wrapper around reference that compares and hashes like a pointer. +/// Can be used as a key in sets/maps indexed by pointers to avoid `unsafe`. +#[derive(Debug)] +pub struct PtrKey<'a, T: 'a>(pub &'a T); + +impl<'a, T> Clone for PtrKey<'a, T> { + fn clone(&self) -> Self { *self } +} + +impl<'a, T> Copy for PtrKey<'a, T> {} + +impl<'a, T> PartialEq for PtrKey<'a, T> { + fn eq(&self, rhs: &Self) -> bool { + ptr::eq(self.0, rhs.0) + } +} + +impl<'a, T> Eq for PtrKey<'a, T> {} + +impl<'a, T> hash::Hash for PtrKey<'a, T> { + fn hash(&self, hasher: &mut H) { + (self.0 as *const T).hash(hasher) + } +} + +impl<'a, T> Deref for PtrKey<'a, T> { + type Target = T; + + fn deref(&self) -> &Self::Target { + self.0 + } +} diff --git a/src/librustc_resolve/resolve_imports.rs b/src/librustc_resolve/resolve_imports.rs index c242b9c7f2f..97b9a385287 100644 --- a/src/librustc_resolve/resolve_imports.rs +++ b/src/librustc_resolve/resolve_imports.rs @@ -17,6 +17,7 @@ use Resolver; use {names_to_string, module_to_string}; use {resolve_error, ResolutionError}; +use rustc_data_structures::ptr_key::PtrKey; use rustc::ty; use rustc::lint::builtin::BuiltinLintDiagnostics; use rustc::lint::builtin::{DUPLICATE_MACRO_EXPORTS, PUB_USE_OF_PRIVATE_EXTERN_CRATE}; @@ -33,7 +34,7 @@ use syntax::util::lev_distance::find_best_match_for_name; use syntax_pos::Span; use std::cell::{Cell, RefCell}; -use std::mem; +use std::{mem, ptr}; /// Contains data for specific types of import directives. #[derive(Clone, Debug)] @@ -105,8 +106,8 @@ impl<'a> ImportDirective<'a> { /// Records information about the resolution of a name in a namespace of a module. pub struct NameResolution<'a> { /// Single imports that may define the name in the namespace. - /// Import directives are arena-allocated, so it's ok to use pointers as keys, they are stable. - single_imports: FxHashSet<*const ImportDirective<'a>>, + /// Import directives are arena-allocated, so it's ok to use pointers as keys. + single_imports: FxHashSet>>, /// The least shadowable known binding for this name, or None if there are no known bindings. pub binding: Option<&'a NameBinding<'a>>, shadowed_glob: Option<&'a NameBinding<'a>>, @@ -192,7 +193,6 @@ impl<'a> Resolver<'a> { // Check if one of single imports can still define the name, // if it can then our result is not determined and can be invalidated. for single_import in &resolution.single_imports { - let single_import = unsafe { &**single_import }; if !self.is_accessible(single_import.vis.get()) { continue; } @@ -291,7 +291,7 @@ impl<'a> Resolver<'a> { SingleImport { target, type_ns_only, .. } => { self.per_ns(|this, ns| if !type_ns_only || ns == TypeNS { let mut resolution = this.resolution(current_module, target, ns).borrow_mut(); - resolution.single_imports.insert(directive); + resolution.single_imports.insert(PtrKey(directive)); }); } // We don't add prelude imports to the globs since they only affect lexical scopes, @@ -398,7 +398,7 @@ impl<'a> Resolver<'a> { _ if old_binding.is_some() => return t, None => return t, Some(binding) => match old_binding { - Some(old_binding) if old_binding as *const _ == binding as *const _ => return t, + Some(old_binding) if ptr::eq(old_binding, binding) => return t, _ => (binding, t), } } @@ -583,7 +583,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { Err(Undetermined) => indeterminate = true, Err(Determined) => { this.update_resolution(parent, target, ns, |_, resolution| { - resolution.single_imports.remove(&(directive as *const _)); + resolution.single_imports.remove(&PtrKey(directive)); }); } Ok(binding) if !binding.is_importable() => { @@ -916,7 +916,7 @@ impl<'a, 'b:'a> ImportResolver<'a, 'b> { let mut reexports = Vec::new(); let mut exported_macro_names = FxHashMap(); - if module as *const _ == self.graph_root as *const _ { + if ptr::eq(module, self.graph_root) { let macro_exports = mem::replace(&mut self.macro_exports, Vec::new()); for export in macro_exports.into_iter().rev() { if let Some(later_span) = exported_macro_names.insert(export.ident.modern(), -- cgit 1.4.1-3-g733a5 From 9f1fdecb3c152b9ca0713f7c85589f9447f28961 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Sun, 22 Jul 2018 22:15:29 +0200 Subject: Simplify vecdeque append test --- src/liballoc/tests/vec_deque.rs | 106 ++++++++++++++++++++++++---------------- 1 file changed, 64 insertions(+), 42 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/vec_deque.rs b/src/liballoc/tests/vec_deque.rs index 0c8c1f2c65b..6efd3d60060 100644 --- a/src/liballoc/tests/vec_deque.rs +++ b/src/liballoc/tests/vec_deque.rs @@ -929,53 +929,75 @@ fn test_append() { } #[test] -fn test_append_advanced() { - fn check( - a_push_back: usize, - a_pop_back: usize, - b_push_back: usize, - b_pop_back: usize, - a_push_front: usize, - a_pop_front: usize, - b_push_front: usize, - b_pop_front: usize - ) { - let mut taken = 0; - let mut a = VecDeque::new(); - let mut b = VecDeque::new(); - for n in (taken..).take(a_push_back) { - a.push_back(n); +fn test_append_permutations() { + fn construct_vec_deque( + push_back: usize, + pop_back: usize, + push_front: usize, + pop_front: usize, + ) -> VecDeque { + let mut out = VecDeque::new(); + for a in 0..push_back { + out.push_back(a); } - taken += a_push_back; - for n in (taken..).take(a_push_front) { - a.push_front(n); + for b in 0..push_front { + out.push_front(push_back + b); } - taken += a_push_front; - for n in (taken..).take(b_push_back) { - b.push_back(n); + for _ in 0..pop_back { + out.pop_back(); } - taken += b_push_back; - for n in (taken..).take(b_push_front) { - b.push_front(n); + for _ in 0..pop_front { + out.pop_front(); } - - a.drain(..a_pop_back); - a.drain(a_pop_front..); - b.drain(..b_pop_back); - b.drain(b_pop_front..); - let checked = a.iter().chain(b.iter()).map(|&x| x).collect::>(); - a.append(&mut b); - assert_eq!(a, checked); - assert!(b.is_empty()); + out } - for a_push in 0..17 { - for a_pop in 0..a_push { - for b_push in 0..17 { - for b_pop in 0..b_push { - check(a_push, a_pop, b_push, b_pop, 0, 0, 0, 0); - check(a_push, a_pop, b_push, b_pop, a_push, 0, 0, 0); - check(a_push, a_pop, b_push, b_pop, 0, 0, b_push, 0); - check(0, 0, 0, 0, a_push, a_pop, b_push, b_pop); + + const MAX: usize = 5; + + // Many different permutations of both the `VecDeque` getting appended to + // and the one getting appended are generated to check `append`. + // This ensures all 6 code paths of `append` are tested. + for src_push_back in 0..MAX { + for src_push_front in 0..MAX { + // doesn't pop more values than are pushed + for src_pop_back in 0..(src_push_back + src_push_front) { + for src_pop_front in 0..(src_push_back + src_push_front - src_pop_back) { + + let src = construct_vec_deque( + src_push_back, + src_pop_back, + src_push_front, + src_pop_front, + ); + + for dst_push_back in 0..MAX { + for dst_push_front in 0..MAX { + for dst_pop_back in 0..(dst_push_back + dst_push_front) { + for dst_pop_front + in 0..(dst_push_back + dst_push_front - dst_pop_back) + { + let mut dst = construct_vec_deque( + dst_push_back, + dst_pop_back, + dst_push_front, + dst_pop_front, + ); + let mut src = src.clone(); + + // Assert that appending `src` to `dst` gives the same order + // of values as iterating over both in sequence. + let correct = dst + .iter() + .chain(src.iter()) + .cloned() + .collect::>(); + dst.append(&mut src); + assert_eq!(dst, correct); + assert!(src.is_empty()); + } + } + } + } } } } -- cgit 1.4.1-3-g733a5 From 6ebd62b8ff86a3ce004894b7fa6d1351f65a5167 Mon Sep 17 00:00:00 2001 From: Pazzaz Date: Sun, 22 Jul 2018 22:18:05 +0200 Subject: Make VecDeque append safer and easier to understand --- src/liballoc/collections/vec_deque.rs | 240 +++++++++++++++++++--------------- 1 file changed, 131 insertions(+), 109 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/collections/vec_deque.rs b/src/liballoc/collections/vec_deque.rs index d0b70b5db2d..7f4fbb99c22 100644 --- a/src/liballoc/collections/vec_deque.rs +++ b/src/liballoc/collections/vec_deque.rs @@ -202,6 +202,20 @@ impl VecDeque { len); } + /// Returns a pair of slices which contain the contents of the buffer not used by the VecDeque. + #[inline] + unsafe fn unused_as_mut_slices<'a>(&'a mut self) -> (&'a mut [T], &'a mut [T]) { + let head = self.head; + let tail = self.tail; + let buf = self.buffer_as_mut_slice(); + if head == tail { + let (before, after) = buf.split_at_mut(head); + (after, before) + } else { + RingSlices::ring_slices(buf, tail, head) + } + } + /// Copies a potentially wrapping block of memory len long from src to dest. /// (abs(dst - src) + len) must be no larger than cap() (There must be at /// most one continuous overlapping region between src and dest). @@ -1834,11 +1848,10 @@ impl VecDeque { #[inline] #[stable(feature = "append", since = "1.4.0")] pub fn append(&mut self, other: &mut Self) { - // Copy from src[i1..i1 + len] to dst[i2..i2 + len]. - // Does not check if the ranges are valid. - unsafe fn copy_part(i1: usize, i2: usize, len: usize, src: &[T], dst: &mut [T]) { - debug_assert!(src.get(i1..i1 + len).is_some() && dst.get(i2..i2 + len).is_some()); - ptr::copy_nonoverlapping(src.as_ptr().add(i1), dst.as_mut_ptr().add(i2), len); + // Copies all values from `src_slice` to the start of `dst_slice`. + unsafe fn copy_whole_slice(src_slice: &[T], dst_slice: &mut [T]) { + let len = src_slice.len(); + ptr::copy_nonoverlapping(src_slice.as_ptr(), dst_slice[..len].as_mut_ptr(), len); } let src_total = other.len(); @@ -1846,132 +1859,141 @@ impl VecDeque { // Guarantees there is space in `self` for `other`. self.reserve(src_total); - self.head = { - let dst_start_1 = self.head; - let src_start_1 = other.tail; - let dst_wrap_point = self.cap(); - let src_wrap_point = other.cap(); - - let dst = unsafe { self.buffer_as_mut_slice() }; - let src = unsafe { other.buffer_as_slice() }; + let new_head = { + let original_head = self.head; - let src_wraps = other.tail > other.head; - let dst_wraps = dst_start_1 + src_total > dst_wrap_point; + // The goal is to copy all values from `other` into `self`. To avoid any + // mismatch, all valid values in `other` are retrieved... + let (src_high, src_low) = other.as_slices(); + // and unoccupied parts of self are retrieved. + let (dst_high, dst_low) = unsafe { self.unused_as_mut_slices() }; - // When minimizing the amount of calls to `copy_part`, there are - // 6 different cases to handle. Whether src and/or dst wrap are 4 - // combinations and there are 3 distinct cases when they both wrap. - // 6 = 3 + 1 + 1 + 1 - match (src_wraps, dst_wraps) { + // Then all that is needed is to copy all values from + // src (src_high and src_low) to dst (dst_high and dst_low). + // + // other [o o o . . . . . o o o o] + // [5 6 7] [1 2 3 4] + // src_low src_high + // + // self [. . . . . . o o o o . .] + // [3 4 5 6 7 .] [1 2] + // dst_low dst_high + // + // Values are not copied one by one but as slices in `copy_whole_slice`. + // What slices are used depends on various properties of src and dst. + // There are 6 cases in total: + // 1. `src` and `dst` are contiguous + // 2. `src` is contiguous and `dst` is discontiguous + // 3. `src` is discontiguous and `dst` is contiguous + // 4. `src` and `dst` are discontiguous + // + src_high is smaller than dst_high + // 5. `src` and `dst` are discontiguous + // + dst_high is smaller than src_high + // 6. `src` and `dst` are discontiguous + // + dst_high is the same size as src_high + let src_contiguous = src_low.is_empty(); + let dst_contiguous = dst_high.len() >= src_total; + match (src_contiguous, dst_contiguous) { (true, true) => { - let dst_before_wrap = dst_wrap_point - dst_start_1; - let src_before_wrap = src_wrap_point - src_start_1; - - if src_before_wrap < dst_before_wrap { - // src - // [o o o . . . . . . o o o] - // 2 3 3 1 1 1 - // - // dst - // [. . . . . . o o . . . .] - // 3 3 H 1 1 1 2 - let src_2 = dst_before_wrap - src_before_wrap; - let dst_start_2 = dst_start_1 + src_before_wrap; - let src_3 = src_total - dst_before_wrap; - - unsafe { - copy_part(src_start_1, dst_start_1, src_before_wrap, src, dst); - copy_part(0, dst_start_2, src_2, src, dst); - copy_part(src_2, 0, src_3, src, dst); - } - src_3 - } else if src_before_wrap > dst_before_wrap { - // src - // [o o o . . . . . o o o o] - // 3 3 3 1 1 2 2 - // - // dst - // [. . . . . . o o o o . .] - // 2 2 3 3 3 H 1 1 - let src_2 = src_before_wrap - dst_before_wrap; - let src_start_2 = src_start_1 + dst_before_wrap; - let src_3 = src_total - src_before_wrap; - - unsafe { - copy_part(src_start_1, dst_start_1, dst_before_wrap, src, dst); - copy_part(src_start_2, 0, src_2, src, dst); - copy_part(0, src_2, src_3, src, dst); - } - src_2 + src_3 - } else { - // src - // [o o . . . . . . . o o o] - // 2 2 1 1 1 - // - // dst - // [. . . . . . . o o . . .] - // 2 2 H 1 1 1 - let src_2 = src_total - src_before_wrap; + // 1. + // other [. . . o o o . . . . . .] + // [] [1 1 1] + // + // self [. o o o o o . . . . . .] + // [.] [1 1 1 . . .] - unsafe { - copy_part(src_start_1, dst_start_1, src_before_wrap, src, dst); - copy_part(0, 0, src_2, src, dst); - } - src_2 + unsafe { + copy_whole_slice(src_high, dst_high); } + original_head + src_total } - (false, true) => { - // src - // [. . . o o o o o . . . .] - // 1 1 2 2 2 + (true, false) => { + // 2. + // other [. . . o o o o o . . . .] + // [] [1 1 2 2 2] // - // dst - // [. . . . . . . o o o . .] - // 2 2 2 H 1 1 - let dst_1 = dst_wrap_point - dst_start_1; - let src_start_2 = src_start_1 + dst_1; - let dst_2 = src_total - dst_1; + // self [. . . . . . . o o o . .] + // [2 2 2 . . . .] [1 1] + let (src_1, src_2) = src_high.split_at(dst_high.len()); unsafe { - copy_part(src_start_1, dst_start_1, dst_1, src, dst); - copy_part(src_start_2, 0, dst_2, src, dst); + copy_whole_slice(src_1, dst_high); + copy_whole_slice(src_2, dst_low); } - dst_2 + src_total - dst_high.len() } - (true, false) => { - // src - // [o o . . . . . . . o o o] - // 2 2 1 1 1 + (false, true) => { + // 3. + // other [o o . . . . . . . o o o] + // [2 2] [1 1 1] // - // dst - // [. o o . . . . . . . . .] - // 1 1 1 2 2 H - let src_1 = src_wrap_point - src_start_1; - let dst_start_2 = dst_start_1 + src_1; - let src_2 = src_total - src_1; + // self [. o o . . . . . . . . .] + // [.] [1 1 1 2 2 . . . .] + let (dst_1, dst_2) = dst_high.split_at_mut(src_high.len()); unsafe { - copy_part(src_start_1, dst_start_1, src_1, src, dst); - copy_part(0, dst_start_2, src_2, src, dst); + copy_whole_slice(src_high, dst_1); + copy_whole_slice(src_low, dst_2); } - dst_start_1 + src_1 + src_2 + original_head + src_total } (false, false) => { - // src - // [. . . o o o . . . . . .] - // 1 1 1 - // - // dst - // [. o o o o o . . . . . .] - // 1 1 1 H - unsafe { - copy_part(src_start_1, dst_start_1, src_total, src, dst); + if src_high.len() < dst_high.len() { + // 4. + // other [o o o . . . . . . o o o] + // [2 3 3] [1 1 1] + // + // self [. . . . . . o o . . . .] + // [3 3 . . . .] [1 1 1 2] + + let (dst_1, dst_2) = dst_high.split_at_mut(src_high.len()); + let (src_2, src_3) = src_low.split_at(dst_2.len()); + unsafe { + copy_whole_slice(src_high, dst_1); + copy_whole_slice(src_2, dst_2); + copy_whole_slice(src_3, dst_low); + } + src_3.len() + } else if src_high.len() > dst_high.len() { + // 5. + // other [o o o . . . . . o o o o] + // [3 3 3] [1 1 2 2] + // + // self [. . . . . . o o o o . .] + // [2 2 3 3 3 .] [1 1] + + let (src_1, src_2) = src_high.split_at(dst_high.len()); + let (dst_2, dst_3) = dst_low.split_at_mut(src_2.len()); + unsafe { + copy_whole_slice(src_1, dst_high); + copy_whole_slice(src_2, dst_2); + copy_whole_slice(src_low, dst_3); + } + dst_2.len() + src_low.len() + } else { + // 6. + // other [o o . . . . . . . o o o] + // [2 2] [1 1 1] + // + // self [. . . . . . . o o . . .] + // [2 2 . . . . .] [1 1 1] + + unsafe { + copy_whole_slice(src_high, dst_high); + copy_whole_slice(src_low, dst_low); + } + src_low.len() } - dst_start_1 + src_total } } }; + + // Up until this point we are in a bad state as some values + // exist in both `self` and `other`. To preserve panic safety + // it is important we clear the old values from `other`... other.clear(); + // and that we update `head` as the last step, making the values accessible in `self`. + self.head = new_head; } /// Retains only the elements specified by the predicate. -- cgit 1.4.1-3-g733a5 From 33b8f6253fe18af5bc882cf885d68538c90dab62 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 23 Jul 2018 11:13:20 +0200 Subject: Don't use NonNull::dangling as sentinel value Instead, rely on alignment and use usize::MAX as sentinel. --- src/liballoc/rc.rs | 13 ++++++++----- src/liballoc/sync.rs | 13 +++++++++---- 2 files changed, 17 insertions(+), 9 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index d76acb28df9..b50bf5c15d5 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -258,6 +258,7 @@ use core::ops::Deref; use core::ops::CoerceUnsized; use core::ptr::{self, NonNull}; use core::convert::From; +use core::usize; use alloc::{Global, Alloc, Layout, box_free, handle_alloc_error}; use string::String; @@ -449,6 +450,8 @@ impl Rc { #[stable(feature = "rc_weak", since = "1.4.0")] pub fn downgrade(this: &Self) -> Weak { this.inc_weak(); + // Make sure we do not create a dangling Weak + debug_assert!(!is_dangling(this.ptr)); Weak { ptr: this.ptr } } @@ -1154,8 +1157,9 @@ impl From> for Rc<[T]> { pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. - // `Weak::new` sets this to a dangling pointer so that it doesn’t need - // to allocate space on the heap. + // `Weak::new` sets this to `usize::MAX` so that it doesn’t need + // to allocate space on the heap. That's not a value a real poiner + // will ever have because RcBox has alignment at least 4. ptr: NonNull>, } @@ -1185,15 +1189,14 @@ impl Weak { #[stable(feature = "downgraded_weak", since = "1.10.0")] pub fn new() -> Weak { Weak { - ptr: NonNull::dangling(), + ptr: NonNull::new(usize::MAX as *mut RcBox).expect("MAX is not 0"), } } } pub(crate) fn is_dangling(ptr: NonNull) -> bool { let address = ptr.as_ptr() as *mut () as usize; - let align = align_of_val(unsafe { ptr.as_ref() }); - address == align + address == usize::MAX } impl Weak { diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 5def0237e7e..4c14fef9b31 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -238,8 +238,9 @@ impl, U: ?Sized> CoerceUnsized> for Arc {} pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. - // `Weak::new` sets this to a dangling pointer so that it doesn’t need - // to allocate space on the heap. + // `Weak::new` sets this to `usize::MAX` so that it doesn’t need + // to allocate space on the heap. That's not a value a real poiner + // will ever have because RcBox has alignment at least 4. ptr: NonNull>, } @@ -442,7 +443,11 @@ impl Arc { // synchronize with the write coming from `is_unique`, so that the // events prior to that write happen before this read. match this.inner().weak.compare_exchange_weak(cur, cur + 1, Acquire, Relaxed) { - Ok(_) => return Weak { ptr: this.ptr }, + Ok(_) => { + // Make sure we do not create a dangling Weak + debug_assert!(!is_dangling(this.ptr)); + return Weak { ptr: this.ptr }; + } Err(old) => cur = old, } } @@ -1033,7 +1038,7 @@ impl Weak { #[stable(feature = "downgraded_weak", since = "1.10.0")] pub fn new() -> Weak { Weak { - ptr: NonNull::dangling(), + ptr: NonNull::new(usize::MAX as *mut ArcInner).expect("MAX is not 0"), } } } -- cgit 1.4.1-3-g733a5 From a303741334baafa475632f23145695cba80ce8e7 Mon Sep 17 00:00:00 2001 From: Ralf Jung Date: Mon, 23 Jul 2018 12:53:37 +0200 Subject: typos --- src/liballoc/rc.rs | 4 ++-- src/liballoc/sync.rs | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/rc.rs b/src/liballoc/rc.rs index b50bf5c15d5..be049eb6e5e 100644 --- a/src/liballoc/rc.rs +++ b/src/liballoc/rc.rs @@ -1158,8 +1158,8 @@ pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. // `Weak::new` sets this to `usize::MAX` so that it doesn’t need - // to allocate space on the heap. That's not a value a real poiner - // will ever have because RcBox has alignment at least 4. + // to allocate space on the heap. That's not a value a real pointer + // will ever have because RcBox has alignment at least 2. ptr: NonNull>, } diff --git a/src/liballoc/sync.rs b/src/liballoc/sync.rs index 4c14fef9b31..a00b6b4e435 100644 --- a/src/liballoc/sync.rs +++ b/src/liballoc/sync.rs @@ -239,8 +239,8 @@ pub struct Weak { // This is a `NonNull` to allow optimizing the size of this type in enums, // but it is not necessarily a valid pointer. // `Weak::new` sets this to `usize::MAX` so that it doesn’t need - // to allocate space on the heap. That's not a value a real poiner - // will ever have because RcBox has alignment at least 4. + // to allocate space on the heap. That's not a value a real pointer + // will ever have because RcBox has alignment at least 2. ptr: NonNull>, } -- cgit 1.4.1-3-g733a5 From 75d22263c92c28510a1bff2a91031d28817443d8 Mon Sep 17 00:00:00 2001 From: tinaun Date: Tue, 24 Jul 2018 15:33:19 -0400 Subject: Impl Executor for Box --- src/liballoc/boxed.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) (limited to 'src/liballoc') diff --git a/src/liballoc/boxed.rs b/src/liballoc/boxed.rs index 6d3bc6e79b5..2cf9b13a67a 100644 --- a/src/liballoc/boxed.rs +++ b/src/liballoc/boxed.rs @@ -67,7 +67,7 @@ use core::marker::{Unpin, Unsize}; use core::mem::{self, PinMut}; use core::ops::{CoerceUnsized, Deref, DerefMut, Generator, GeneratorState}; use core::ptr::{self, NonNull, Unique}; -use core::task::{Context, Poll}; +use core::task::{Context, Poll, Executor, SpawnErrorKind, SpawnObjError}; use raw_vec::RawVec; use str::from_boxed_utf8_unchecked; @@ -972,6 +972,19 @@ unsafe impl<'a, T, F> UnsafeFutureObj<'a, T> for PinBox } } +#[unstable(feature = "futures_api", issue = "50547")] +impl Executor for Box + where E: Executor + ?Sized +{ + fn spawn_obj(&mut self, task: FutureObj<'static, ()>) -> Result<(), SpawnObjError> { + (**self).spawn_obj(task) + } + + fn status(&self) -> Result<(), SpawnErrorKind> { + (**self).status() + } +} + #[unstable(feature = "futures_api", issue = "50547")] impl<'a, F: Future + Send + 'a> From> for FutureObj<'a, ()> { fn from(boxed: PinBox) -> Self { -- cgit 1.4.1-3-g733a5 From 66c4dc9769b7ae456fe2960dc5a1e037b2432184 Mon Sep 17 00:00:00 2001 From: Tatsuyuki Ishi Date: Fri, 13 Jul 2018 14:25:22 +0900 Subject: Add missing dyn --- src/liballoc/tests/arc.rs | 6 +++--- src/liballoc/tests/btree/set.rs | 2 +- src/liballoc/tests/lib.rs | 2 +- src/liballoc/tests/rc.rs | 6 +++--- src/libcore/tests/any.rs | 16 +++++++++------ src/libcore/tests/hash/mod.rs | 2 +- src/libcore/tests/intrinsics.rs | 2 +- src/libcore/tests/mem.rs | 4 ++-- src/libcore/tests/option.rs | 2 +- src/libcore/tests/ptr.rs | 20 +++++++++--------- src/libcore/tests/result.rs | 2 +- src/librustdoc/clean/mod.rs | 2 +- src/librustdoc/core.rs | 2 +- src/librustdoc/html/highlight.rs | 4 ++-- src/librustdoc/html/layout.rs | 4 ++-- src/librustdoc/html/render.rs | 2 +- src/librustdoc/test.rs | 2 +- src/libstd/sys/redox/process.rs | 4 ++-- src/tools/compiletest/src/header.rs | 2 +- src/tools/compiletest/src/read2.rs | 6 +++--- src/tools/error_index_generator/main.rs | 36 ++++++++++++++++----------------- src/tools/tidy/src/features.rs | 2 +- src/tools/tidy/src/lib.rs | 4 ++-- 23 files changed, 69 insertions(+), 65 deletions(-) (limited to 'src/liballoc') diff --git a/src/liballoc/tests/arc.rs b/src/liballoc/tests/arc.rs index 753873dd294..d90c22a3b18 100644 --- a/src/liballoc/tests/arc.rs +++ b/src/liballoc/tests/arc.rs @@ -18,7 +18,7 @@ fn uninhabited() { a = a.clone(); assert!(a.upgrade().is_none()); - let mut a: Weak = a; // Unsizing + let mut a: Weak = a; // Unsizing a = a.clone(); assert!(a.upgrade().is_none()); } @@ -39,7 +39,7 @@ fn slice() { #[test] fn trait_object() { let a: Arc = Arc::new(4); - let a: Arc = a; // Unsizing + let a: Arc = a; // Unsizing // Exercise is_dangling() with a DST let mut a = Arc::downgrade(&a); @@ -49,7 +49,7 @@ fn trait_object() { let mut b = Weak::::new(); b = b.clone(); assert!(b.upgrade().is_none()); - let mut b: Weak = b; // Unsizing + let mut b: Weak = b; // Unsizing b = b.clone(); assert!(b.upgrade().is_none()); } diff --git a/src/liballoc/tests/btree/set.rs b/src/liballoc/tests/btree/set.rs index 6171b8ba624..0330bda5e32 100644 --- a/src/liballoc/tests/btree/set.rs +++ b/src/liballoc/tests/btree/set.rs @@ -40,7 +40,7 @@ fn test_hash() { } fn check(a: &[i32], b: &[i32], expected: &[i32], f: F) - where F: FnOnce(&BTreeSet, &BTreeSet, &mut FnMut(&i32) -> bool) -> bool + where F: FnOnce(&BTreeSet, &BTreeSet, &mut dyn FnMut(&i32) -> bool) -> bool { let mut set_a = BTreeSet::new(); let mut set_b = BTreeSet::new(); diff --git a/src/liballoc/tests/lib.rs b/src/liballoc/tests/lib.rs index 2c361598e89..91bc778ad4c 100644 --- a/src/liballoc/tests/lib.rs +++ b/src/liballoc/tests/lib.rs @@ -63,7 +63,7 @@ fn test_boxed_hasher() { 5u32.hash(&mut hasher_1); assert_eq!(ordinary_hash, hasher_1.finish()); - let mut hasher_2 = Box::new(DefaultHasher::new()) as Box; + let mut hasher_2 = Box::new(DefaultHasher::new()) as Box; 5u32.hash(&mut hasher_2); assert_eq!(ordinary_hash, hasher_2.finish()); } diff --git a/src/liballoc/tests/rc.rs b/src/liballoc/tests/rc.rs index baa0406acfc..9ec7c831444 100644 --- a/src/liballoc/tests/rc.rs +++ b/src/liballoc/tests/rc.rs @@ -18,7 +18,7 @@ fn uninhabited() { a = a.clone(); assert!(a.upgrade().is_none()); - let mut a: Weak = a; // Unsizing + let mut a: Weak = a; // Unsizing a = a.clone(); assert!(a.upgrade().is_none()); } @@ -39,7 +39,7 @@ fn slice() { #[test] fn trait_object() { let a: Rc = Rc::new(4); - let a: Rc = a; // Unsizing + let a: Rc = a; // Unsizing // Exercise is_dangling() with a DST let mut a = Rc::downgrade(&a); @@ -49,7 +49,7 @@ fn trait_object() { let mut b = Weak::::new(); b = b.clone(); assert!(b.upgrade().is_none()); - let mut b: Weak = b; // Unsizing + let mut b: Weak = b; // Unsizing b = b.clone(); assert!(b.upgrade().is_none()); } diff --git a/src/libcore/tests/any.rs b/src/libcore/tests/any.rs index 2d3e81aa131..a80bf939530 100644 --- a/src/libcore/tests/any.rs +++ b/src/libcore/tests/any.rs @@ -17,7 +17,7 @@ static TEST: &'static str = "Test"; #[test] fn any_referenced() { - let (a, b, c) = (&5 as &Any, &TEST as &Any, &Test as &Any); + let (a, b, c) = (&5 as &dyn Any, &TEST as &dyn Any, &Test as &dyn Any); assert!(a.is::()); assert!(!b.is::()); @@ -34,7 +34,11 @@ fn any_referenced() { #[test] fn any_owning() { - let (a, b, c) = (box 5_usize as Box, box TEST as Box, box Test as Box); + let (a, b, c) = ( + box 5_usize as Box, + box TEST as Box, + box Test as Box, + ); assert!(a.is::()); assert!(!b.is::()); @@ -51,7 +55,7 @@ fn any_owning() { #[test] fn any_downcast_ref() { - let a = &5_usize as &Any; + let a = &5_usize as &dyn Any; match a.downcast_ref::() { Some(&5) => {} @@ -69,9 +73,9 @@ fn any_downcast_mut() { let mut a = 5_usize; let mut b: Box<_> = box 7_usize; - let a_r = &mut a as &mut Any; + let a_r = &mut a as &mut dyn Any; let tmp: &mut usize = &mut *b; - let b_r = tmp as &mut Any; + let b_r = tmp as &mut dyn Any; match a_r.downcast_mut::() { Some(x) => { @@ -113,7 +117,7 @@ fn any_downcast_mut() { #[test] fn any_fixed_vec() { let test = [0_usize; 8]; - let test = &test as &Any; + let test = &test as &dyn Any; assert!(test.is::<[usize; 8]>()); assert!(!test.is::<[usize; 10]>()); } diff --git a/src/libcore/tests/hash/mod.rs b/src/libcore/tests/hash/mod.rs index 8716421b424..85c9d41b65b 100644 --- a/src/libcore/tests/hash/mod.rs +++ b/src/libcore/tests/hash/mod.rs @@ -128,7 +128,7 @@ fn test_custom_state() { fn test_indirect_hasher() { let mut hasher = MyHasher { hash: 0 }; { - let mut indirect_hasher: &mut Hasher = &mut hasher; + let mut indirect_hasher: &mut dyn Hasher = &mut hasher; 5u32.hash(&mut indirect_hasher); } assert_eq!(hasher.hash, 5); diff --git a/src/libcore/tests/intrinsics.rs b/src/libcore/tests/intrinsics.rs index 2b380abf63c..9f3cba26a62 100644 --- a/src/libcore/tests/intrinsics.rs +++ b/src/libcore/tests/intrinsics.rs @@ -22,7 +22,7 @@ fn test_typeid_sized_types() { #[test] fn test_typeid_unsized_types() { trait Z {} - struct X(str); struct Y(Z + 'static); + struct X(str); struct Y(dyn Z + 'static); assert_eq!(TypeId::of::(), TypeId::of::()); assert_eq!(TypeId::of::(), TypeId::of::()); diff --git a/src/libcore/tests/mem.rs b/src/libcore/tests/mem.rs index f55a1c81463..714f2babbdf 100644 --- a/src/libcore/tests/mem.rs +++ b/src/libcore/tests/mem.rs @@ -109,11 +109,11 @@ fn test_transmute() { trait Foo { fn dummy(&self) { } } impl Foo for isize {} - let a = box 100isize as Box; + let a = box 100isize as Box; unsafe { let x: ::core::raw::TraitObject = transmute(a); assert!(*(x.data as *const isize) == 100); - let _x: Box = transmute(x); + let _x: Box = transmute(x); } unsafe { diff --git a/src/libcore/tests/option.rs b/src/libcore/tests/option.rs index bc3e61a4f54..324ebf43565 100644 --- a/src/libcore/tests/option.rs +++ b/src/libcore/tests/option.rs @@ -240,7 +240,7 @@ fn test_collect() { assert!(v == None); // test that it does not take more elements than it needs - let mut functions: [Box Option<()>>; 3] = + let mut functions: [Box Option<()>>; 3] = [box || Some(()), box || None, box || panic!()]; let v: Option> = functions.iter_mut().map(|f| (*f)()).collect(); diff --git a/src/libcore/tests/ptr.rs b/src/libcore/tests/ptr.rs index 31bc1d67768..92160910d8f 100644 --- a/src/libcore/tests/ptr.rs +++ b/src/libcore/tests/ptr.rs @@ -84,16 +84,16 @@ fn test_is_null() { assert!(nms.is_null()); // Pointers to unsized types -- trait objects - let ci: *const ToString = &3; + let ci: *const dyn ToString = &3; assert!(!ci.is_null()); - let mi: *mut ToString = &mut 3; + let mi: *mut dyn ToString = &mut 3; assert!(!mi.is_null()); - let nci: *const ToString = null::(); + let nci: *const dyn ToString = null::(); assert!(nci.is_null()); - let nmi: *mut ToString = null_mut::(); + let nmi: *mut dyn ToString = null_mut::(); assert!(nmi.is_null()); } @@ -140,16 +140,16 @@ fn test_as_ref() { assert_eq!(nms.as_ref(), None); // Pointers to unsized types -- trait objects - let ci: *const ToString = &3; + let ci: *const dyn ToString = &3; assert!(ci.as_ref().is_some()); - let mi: *mut ToString = &mut 3; + let mi: *mut dyn ToString = &mut 3; assert!(mi.as_ref().is_some()); - let nci: *const ToString = null::(); + let nci: *const dyn ToString = null::(); assert!(nci.as_ref().is_none()); - let nmi: *mut ToString = null_mut::(); + let nmi: *mut dyn ToString = null_mut::(); assert!(nmi.as_ref().is_none()); } } @@ -182,10 +182,10 @@ fn test_as_mut() { assert_eq!(nms.as_mut(), None); // Pointers to unsized types -- trait objects - let mi: *mut ToString = &mut 3; + let mi: *mut dyn ToString = &mut 3; assert!(mi.as_mut().is_some()); - let nmi: *mut ToString = null_mut::(); + let nmi: *mut dyn ToString = null_mut::(); assert!(nmi.as_mut().is_none()); } } diff --git a/src/libcore/tests/result.rs b/src/libcore/tests/result.rs index d9927ce4487..0616252c82c 100644 --- a/src/libcore/tests/result.rs +++ b/src/libcore/tests/result.rs @@ -81,7 +81,7 @@ fn test_collect() { assert!(v == Err(2)); // test that it does not take more elements than it needs - let mut functions: [Box Result<(), isize>>; 3] = + let mut functions: [Box Result<(), isize>>; 3] = [box || Ok(()), box || Err(1), box || panic!()]; let v: Result, isize> = functions.iter_mut().map(|f| (*f)()).collect(); diff --git a/src/librustdoc/clean/mod.rs b/src/librustdoc/clean/mod.rs index 98684b27973..c601f138d0a 100644 --- a/src/librustdoc/clean/mod.rs +++ b/src/librustdoc/clean/mod.rs @@ -375,7 +375,7 @@ impl fmt::Debug for Item { let fake = MAX_DEF_ID.with(|m| m.borrow().get(&self.def_id.krate) .map(|id| self.def_id >= *id).unwrap_or(false)); - let def_id: &fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id }; + let def_id: &dyn fmt::Debug = if fake { &"**FAKE**" } else { &self.def_id }; fmt.debug_struct("Item") .field("source", &self.source) diff --git a/src/librustdoc/core.rs b/src/librustdoc/core.rs index 6e1b4589547..4b8dbaf4211 100644 --- a/src/librustdoc/core.rs +++ b/src/librustdoc/core.rs @@ -55,7 +55,7 @@ pub struct DocContext<'a, 'tcx: 'a, 'rcx: 'a> { /// The stack of module NodeIds up till this point pub mod_ids: RefCell>, pub crate_name: Option, - pub cstore: Rc, + pub cstore: Rc, pub populated_all_crate_impls: Cell, // Note that external items for which `doc(hidden)` applies to are shown as // non-reachable while local items aren't. This is because we're reusing diff --git a/src/librustdoc/html/highlight.rs b/src/librustdoc/html/highlight.rs index 5939d5341e3..6cf9b143373 100644 --- a/src/librustdoc/html/highlight.rs +++ b/src/librustdoc/html/highlight.rs @@ -395,7 +395,7 @@ impl Class { fn write_header(class: Option<&str>, id: Option<&str>, - out: &mut Write) + out: &mut dyn Write) -> io::Result<()> { write!(out, "

,
     write!(out, "class=\"rust {}\">\n", class.unwrap_or(""))
 }
 
-fn write_footer(out: &mut Write) -> io::Result<()> {
+fn write_footer(out: &mut dyn Write) -> io::Result<()> {
     write!(out, "
\n") } diff --git a/src/librustdoc/html/layout.rs b/src/librustdoc/html/layout.rs index 5e93b20ea17..af7c0a04215 100644 --- a/src/librustdoc/html/layout.rs +++ b/src/librustdoc/html/layout.rs @@ -32,7 +32,7 @@ pub struct Page<'a> { } pub fn render( - dst: &mut io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T, + dst: &mut dyn io::Write, layout: &Layout, page: &Page, sidebar: &S, t: &T, css_file_extension: bool, themes: &[PathBuf]) -> io::Result<()> { @@ -194,7 +194,7 @@ pub fn render( ) } -pub fn redirect(dst: &mut io::Write, url: &str) -> io::Result<()> { +pub fn redirect(dst: &mut dyn io::Write, url: &str) -> io::Result<()> { //