check-stage$(1)-T-$(2)-H-$(3)-ui-exec \
check-stage$(1)-T-$(2)-H-$(3)-doc-exec \
check-stage$(1)-T-$(2)-H-$(3)-doc-error-index-exec \
- check-stage$(1)-T-$(2)-H-$(3)-pretty-exec
+ check-stage$(1)-T-$(2)-H-$(3)-pretty-exec \
+ check-stage$(1)-T-$(2)-H-$(3)-mir-opt-exec
ifndef CFG_DISABLE_CODEGEN_TESTS
check-stage$(1)-T-$(2)-H-$(3)-exec: \
$(call rwildcard,$(S)src/test/ui/,*.stdout) \
$(call rwildcard,$(S)src/test/ui/,*.stderr)
RUSTDOCCK_RS := $(call rwildcard,$(S)src/test/rustdoc/,*.rs)
+MIR_OPT_RS := $(call rwildcard,$(S)src/test/mir-opt/,*.rs)
RPASS_TESTS := $(RPASS_RS)
RPASS_VALGRIND_TESTS := $(RPASS_VALGRIND_RS)
INCREMENTAL_TESTS := $(INCREMENTAL_RS)
RMAKE_TESTS := $(RMAKE_RS)
UI_TESTS := $(UI_RS)
+MIR_OPT_TESTS := $(MIR_OPT_RS)
RUSTDOCCK_TESTS := $(RUSTDOCCK_RS)
CTEST_SRC_BASE_rpass = run-pass
CTEST_MODE_ui = ui
CTEST_RUNTOOL_ui = $(CTEST_RUNTOOL)
+CTEST_SRC_BASE_mir-opt = mir-opt
+CTEST_BUILD_BASE_mir-opt = mir-opt
+CTEST_MODE_mir-opt = mir-opt
+CTEST_RUNTOOL_mir-opt = $(CTEST_RUNTOOL)
+
CTEST_SRC_BASE_rustdocck = rustdoc
CTEST_BUILD_BASE_rustdocck = rustdoc
CTEST_MODE_rustdocck = rustdoc
CTEST_DEPS_rmake_$(1)-T-$(2)-H-$(3) = $$(RMAKE_TESTS) \
$$(CSREQ$(1)_T_$(3)_H_$(3)) $$(SREQ$(1)_T_$(2)_H_$(3))
CTEST_DEPS_ui_$(1)-T-$(2)-H-$(3) = $$(UI_TESTS)
+CTEST_DEPS_mir-opt_$(1)-T-$(2)-H-$(3) = $$(MIR_OPT_TESTS)
CTEST_DEPS_rustdocck_$(1)-T-$(2)-H-$(3) = $$(RUSTDOCCK_TESTS) \
$$(HBIN$(1)_H_$(3))/rustdoc$$(X_$(3)) \
$(S)src/etc/htmldocck.py
CTEST_NAMES = rpass rpass-valgrind rpass-full rfail-full cfail-full rfail cfail pfail \
debuginfo-gdb debuginfo-lldb codegen codegen-units rustdocck incremental \
- rmake ui
+ rmake ui mir-opt
$(foreach host,$(CFG_HOST), \
$(eval $(foreach target,$(CFG_TARGET), \
pretty-rfail-full \
pretty-rfail \
pretty-pretty \
+ mir-opt \
$(NULL)
define DEF_CHECK_FOR_STAGE_AND_TARGET_AND_HOST
check::compiletest(self, &compiler, target.target,
"pretty", "run-pass-valgrind");
}
+ CheckMirOpt { compiler } => {
+ check::compiletest(self, &compiler, target.target,
+ "mir-opt", "mir-opt");
+ }
CheckCodegen { compiler } => {
check::compiletest(self, &compiler, target.target,
"codegen", "codegen");
(check_codegen_units, CheckCodegenUnits { compiler: Compiler<'a> }),
(check_incremental, CheckIncremental { compiler: Compiler<'a> }),
(check_ui, CheckUi { compiler: Compiler<'a> }),
+ (check_mir_opt, CheckMirOpt { compiler: Compiler<'a> }),
(check_debuginfo, CheckDebuginfo { compiler: Compiler<'a> }),
(check_rustdoc, CheckRustdoc { compiler: Compiler<'a> }),
(check_docs, CheckDocs { compiler: Compiler<'a> }),
self.check_pretty_rfail_full(compiler),
self.check_rpass_valgrind(compiler),
self.check_rmake(compiler),
+ self.check_mir_opt(compiler),
// crates
self.check_crate_rustc(compiler),
Source::CheckTidy { stage } => {
vec![self.tool_tidy(stage)]
}
+ Source::CheckMirOpt { compiler} |
Source::CheckPrettyRPass { compiler } |
Source::CheckPrettyRFail { compiler } |
Source::CheckRFail { compiler } |
#### On ranges:
```rust
-for (i, j) in (5..10).enumerate() {
- println!("i = {} and j = {}", i, j);
+for (index, value) in (5..10).enumerate() {
+ println!("index = {} and value = {}", index, value);
}
```
Outputs:
```text
-i = 0 and j = 5
-i = 1 and j = 6
-i = 2 and j = 7
-i = 3 and j = 8
-i = 4 and j = 9
+index = 0 and value = 5
+index = 1 and value = 6
+index = 2 and value = 7
+index = 3 and value = 8
+index = 4 and value = 9
```
Don't forget to add the parentheses around the range.
}
/// 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
}
}
-/// A vacant Entry.
+/// A vacant Entry. 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,
}
}
-/// An occupied Entry.
+/// An occupied Entry. 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<NodeRef<marker::Mut<'a>, K, V, marker::LeafOrInternal>, marker::KV>,
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 {
/// 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_owned();
+ ///
+ /// map.entry("poneyland").or_insert_with(|| s);
+ ///
+ /// assert_eq!(map["poneyland"], "hoho".to_owned());
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn or_insert_with<F: FnOnce() -> V>(self, default: F) -> &'a mut V {
match self {
}
/// 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 {
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
+ ///
+ /// ```
+ /// #![feature(map_entry_recover_keys)]
+ ///
+ /// 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();
+ /// }
+ /// ```
#[unstable(feature = "map_entry_recover_keys", issue = "34285")]
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;
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
+ ///
+ /// ```
+ /// #![feature(map_entry_recover_keys)]
+ ///
+ /// 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_pair();
+ /// }
+ ///
+ /// // If now try to get the value, it will panic:
+ /// // println!("{}", map["poneyland"]);
+ /// ```
#[unstable(feature = "map_entry_recover_keys", issue = "34285")]
pub fn remove_pair(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.
+ ///
+ /// # 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!(map["poneyland"], 22);
+ /// ```
#[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.
+ ///
+ /// # 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
//! ## Precision
//!
//! For non-numeric types, this can be considered a "maximum width". If the resulting string is
-//! longer than this width, then it is truncated down to this many characters and only those are
-//! emitted.
+//! longer than this width, then it is truncated down to this many characters and that truncated
+//! value is emitted with proper `fill`, `alignment` and `width` if those parameters are set.
//!
//! For integral types, this is ignored.
//!
//! ```
//! println!("{}, `{name:.*}` has 3 fractional digits", "Hello", 3, name=1234.56);
//! println!("{}, `{name:.*}` has 3 characters", "Hello", 3, name="1234.56");
+//! println!("{}, `{name:>8.*}` has 3 right-aligned characters", "Hello", 3, name="1234.56");
//! ```
//!
//! print two significantly different things:
//! ```text
//! Hello, `1234.560` has 3 fractional digits
//! Hello, `123` has 3 characters
+//! Hello, ` 123` has 3 right-aligned characters
//! ```
//!
//! # Escaping
impl<T> LinkedList<T> {
/// Creates an empty `LinkedList`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::LinkedList;
+ ///
+ /// let list: LinkedList<u32> = LinkedList::new();
+ /// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> Self {
}
/// Provides a forward iterator.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::LinkedList;
+ ///
+ /// let mut list: LinkedList<u32> = 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<T> {
}
/// Provides a forward iterator with mutable references.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::LinkedList;
+ ///
+ /// let mut list: LinkedList<u32> = 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<T> {
///
/// dl.push_back(3);
/// assert_eq!(dl.len(), 3);
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
/// dl.clear();
/// assert_eq!(dl.len(), 0);
/// assert_eq!(dl.front(), None);
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
/// Returns `true` if the `LinkedList` contains an element equal to the
/// given value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(linked_list_contains)]
+ ///
+ /// use std::collections::LinkedList;
+ ///
+ /// let mut list: LinkedList<u32> = 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);
+ /// ```
#[unstable(feature = "linked_list_contains", reason = "recently added",
issue = "32630")]
pub fn contains(&self, x: &T) -> bool
///
/// dl.push_front(1);
/// assert_eq!(dl.front(), Some(&1));
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
/// Some(x) => *x = 5,
/// }
/// assert_eq!(dl.front(), Some(&5));
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
///
/// dl.push_back(1);
/// assert_eq!(dl.back(), Some(&1));
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
/// Some(x) => *x = 5,
/// }
/// assert_eq!(dl.back(), Some(&5));
- ///
/// ```
#[inline]
#[stable(feature = "rust1", since = "1.0.0")]
///
/// 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) {
/// 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<T> {
self.pop_front_node().map(Node::into_element)
use core::hash;
use core::iter::FromIterator;
use core::mem;
-use core::ops::{self, Add, Index, IndexMut};
+use core::ops::{self, Add, AddAssign, Index, IndexMut};
use core::ptr;
use core::str::pattern::Pattern;
use rustc_unicode::char::{decode_utf16, REPLACEMENT_CHARACTER};
assert!(idx <= len);
assert!(self.is_char_boundary(idx));
let bits = ch.encode_utf8();
- let bits = bits.as_slice();
- let amt = bits.len();
+
+ unsafe {
+ self.insert_bytes(idx, bits.as_slice());
+ }
+ }
+
+ unsafe fn insert_bytes(&mut self, idx: usize, bytes: &[u8]) {
+ let len = self.len();
+ let amt = bytes.len();
self.vec.reserve(amt);
+ ptr::copy(self.vec.as_ptr().offset(idx as isize),
+ self.vec.as_mut_ptr().offset((idx + amt) as isize),
+ len - idx);
+ ptr::copy(bytes.as_ptr(),
+ self.vec.as_mut_ptr().offset(idx as isize),
+ amt);
+ self.vec.set_len(len + amt);
+ }
+
+ /// Inserts a string into this `String` at a byte position.
+ ///
+ /// This is an `O(n)` operation as it requires copying every element in the
+ /// buffer.
+ ///
+ /// # Panics
+ ///
+ /// Panics if `idx` is larger than the `String`'s length, or if it does not
+ /// lie on a [`char`] boundary.
+ ///
+ /// [`char`]: ../../std/primitive.char.html
+ ///
+ /// # Examples
+ ///
+ /// Basic usage:
+ ///
+ /// ```
+ /// #![feature(insert_str)]
+ ///
+ /// let mut s = String::from("bar");
+ ///
+ /// s.insert_str(0, "foo");
+ ///
+ /// assert_eq!("foobar", s);
+ /// ```
+ #[inline]
+ #[unstable(feature = "insert_str",
+ reason = "recent addition",
+ issue = "0")]
+ pub fn insert_str(&mut self, idx: usize, string: &str) {
+ let len = self.len();
+ assert!(idx <= len);
+ assert!(self.is_char_boundary(idx));
+
unsafe {
- ptr::copy(self.vec.as_ptr().offset(idx as isize),
- self.vec.as_mut_ptr().offset((idx + amt) as isize),
- len - idx);
- ptr::copy(bits.as_ptr(),
- self.vec.as_mut_ptr().offset(idx as isize),
- amt);
- self.vec.set_len(len + amt);
+ self.insert_bytes(idx, string.as_bytes());
}
}
}
}
+#[stable(feature = "stringaddassign", since = "1.12.0")]
+impl<'a> AddAssign<&'a str> for String {
+ #[inline]
+ fn add_assign(&mut self, other: &str) {
+ self.push_str(other);
+ }
+}
+
#[stable(feature = "rust1", since = "1.0.0")]
impl ops::Index<ops::Range<usize>> for String {
type Output = str;
/// Extracts a slice containing the entire vector.
///
/// Equivalent to `&s[..]`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::{self, Write};
+ /// let buffer = vec![1, 2, 3, 5, 8];
+ /// io::sink().write(buffer.as_slice()).unwrap();
+ /// ```
#[inline]
#[stable(feature = "vec_as_slice", since = "1.7.0")]
pub fn as_slice(&self) -> &[T] {
/// Extracts a mutable slice of the entire vector.
///
/// Equivalent to `&mut s[..]`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::io::{self, Read};
+ /// let mut buffer = vec![0; 3];
+ /// io::repeat(0b101).read_exact(buffer.as_mut_slice()).unwrap();
+ /// ```
#[inline]
#[stable(feature = "vec_as_slice", since = "1.7.0")]
pub fn as_mut_slice(&mut self) -> &mut [T] {
/// # Examples
///
/// ```
- /// let mut v = vec![1, 2, 3, 4];
+ /// use std::ptr;
+ ///
+ /// let mut vec = vec!['r', 'u', 's', 't'];
+ ///
+ /// unsafe {
+ /// ptr::drop_in_place(&mut vec[3]);
+ /// vec.set_len(3);
+ /// }
+ /// assert_eq!(vec, ['r', 'u', 's']);
+ /// ```
+ ///
+ /// In this example, there is a memory leak since the memory locations
+ /// owned by the vector were not freed prior to the `set_len` call:
+ ///
+ /// ```
+ /// let mut vec = vec!['r', 'u', 's', 't'];
+ ///
+ /// unsafe {
+ /// vec.set_len(0);
+ /// }
+ /// ```
+ ///
+ /// In this example, the vector gets expanded from zero to four items
+ /// without any memory allocations occurring, resulting in vector
+ /// values of unallocated memory:
+ ///
+ /// ```
+ /// let mut vec: Vec<char> = Vec::new();
+ ///
/// unsafe {
- /// v.set_len(1);
+ /// vec.set_len(4);
/// }
/// ```
#[inline]
impl<T> VecDeque<T> {
/// Creates an empty `VecDeque`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::VecDeque;
+ ///
+ /// let vector: VecDeque<u32> = VecDeque::new();
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn new() -> VecDeque<T> {
VecDeque::with_capacity(INITIAL_CAPACITY)
}
/// Creates an empty `VecDeque` with space for at least `n` elements.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::VecDeque;
+ ///
+ /// let vector: VecDeque<u32> = VecDeque::with_capacity(10);
+ /// ```
#[stable(feature = "rust1", since = "1.0.0")]
pub fn with_capacity(n: usize) -> VecDeque<T> {
// +1 since the ringbuffer always leaves one space empty
/// Returns a pair of slices which contain, in order, the contents of the
/// `VecDeque`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::VecDeque;
+ ///
+ /// let mut vector: VecDeque<u32> = VecDeque::new();
+ ///
+ /// vector.push_back(0);
+ /// vector.push_back(1);
+ /// vector.push_back(2);
+ ///
+ /// assert_eq!(vector.as_slices(), (&[0u32, 1, 2] as &[u32], &[] as &[u32]));
+ ///
+ /// vector.push_front(10);
+ /// vector.push_front(9);
+ ///
+ /// assert_eq!(vector.as_slices(), (&[9u32, 10] as &[u32], &[0u32, 1, 2] as &[u32]));
+ /// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]
pub fn as_slices(&self) -> (&[T], &[T]) {
/// Returns a pair of slices which contain, in order, the contents of the
/// `VecDeque`.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::VecDeque;
+ ///
+ /// let mut vector: VecDeque<u32> = 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(), (&[42u32, 10] as &[u32], &[24u32, 1] as &[u32]));
+ /// ```
#[inline]
#[stable(feature = "deque_extras_15", since = "1.5.0")]
pub fn as_mut_slices(&mut self) -> (&mut [T], &mut [T]) {
///
/// ```
/// use std::collections::VecDeque;
-
+ ///
/// let mut v: VecDeque<_> = vec![1, 2, 3].into_iter().collect();
/// assert_eq!(vec![3].into_iter().collect::<VecDeque<_>>(), v.drain(2..).collect());
/// assert_eq!(vec![1, 2].into_iter().collect::<VecDeque<_>>(), v);
/// Returns `true` if the `VecDeque` contains an element equal to the
/// given value.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// #![feature(vec_deque_contains)]
+ ///
+ /// use std::collections::VecDeque;
+ ///
+ /// let mut vector: VecDeque<u32> = VecDeque::new();
+ ///
+ /// vector.push_back(0);
+ /// vector.push_back(1);
+ ///
+ /// assert_eq!(vector.contains(&1), true);
+ /// assert_eq!(vector.contains(&10), false);
+ /// ```
#[unstable(feature = "vec_deque_contains", reason = "recently added",
issue = "32630")]
pub fn contains(&self, x: &T) -> bool
/// Returns `None` if `index` is out of bounds.
///
/// # Examples
+ ///
/// ```
/// use std::collections::VecDeque;
///
return self.buf.write_str(s);
}
// The `precision` field can be interpreted as a `max-width` for the
- // string being formatted
- if let Some(max) = self.precision {
- // If there's a maximum width and our string is longer than
- // that, then we must always have truncation. This is the only
- // case where the maximum length will matter.
+ // string being formatted.
+ let s = if let Some(max) = self.precision {
+ // If our string is longer that the precision, then we must have
+ // truncation. However other flags like `fill`, `width` and `align`
+ // must act as always.
if let Some((i, _)) = s.char_indices().skip(max).next() {
- return self.buf.write_str(&s[..i])
+ &s[..i]
+ } else {
+ &s
}
- }
+ } else {
+ &s
+ };
// The `width` field is more of a `min-width` parameter at this point.
match self.width {
// If we're under the maximum length, and there's no minimum length
use self::Option::*;
use clone::Clone;
+use convert::From;
use default::Default;
use iter::ExactSizeIterator;
use iter::{Iterator, DoubleEndedIterator, FromIterator, IntoIterator};
}
}
+#[stable(since = "1.12.0", feature = "option_from")]
+impl<T> From<T> for Option<T> {
+ fn from(val: T) -> Option<T> {
+ Some(val)
+ }
+}
+
/////////////////////////////////////////////////////////////////////////////
// The Option Iterators
/////////////////////////////////////////////////////////////////////////////
pub const DW_EH_PE_indirect: u8 = 0x80;
#[derive(Copy, Clone)]
-pub struct EHContext {
+pub struct EHContext<'a> {
pub ip: usize, // Current instruction pointer
pub func_start: usize, // Address of the current function
- pub text_start: usize, // Address of the code section
- pub data_start: usize, // Address of the data section
+ pub get_text_start: &'a Fn() -> usize, // Get address of the code section
+ pub get_data_start: &'a Fn() -> usize, // Get address of the data section
}
-pub unsafe fn find_landing_pad(lsda: *const u8, context: &EHContext) -> Option<usize> {
+pub enum EHAction {
+ None,
+ Cleanup(usize),
+ Catch(usize),
+ Terminate,
+}
+
+pub const USING_SJLJ_EXCEPTIONS: bool = cfg!(all(target_os = "ios", target_arch = "arm"));
+
+pub unsafe fn find_eh_action(lsda: *const u8, context: &EHContext) -> EHAction {
if lsda.is_null() {
- return None;
+ return EHAction::None;
}
let func_start = context.func_start;
let call_site_encoding = reader.read::<u8>();
let call_site_table_length = reader.read_uleb128();
let action_table = reader.ptr.offset(call_site_table_length as isize);
- // Return addresses point 1 byte past the call instruction, which could
- // be in the next IP range.
- let ip = context.ip - 1;
-
- while reader.ptr < action_table {
- let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
- let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
- let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
- let cs_action = reader.read_uleb128();
- // Callsite table is sorted by cs_start, so if we've passed the ip, we
- // may stop searching.
- if ip < func_start + cs_start {
- break;
+ let ip = context.ip;
+
+ if !USING_SJLJ_EXCEPTIONS {
+ while reader.ptr < action_table {
+ let cs_start = read_encoded_pointer(&mut reader, context, call_site_encoding);
+ let cs_len = read_encoded_pointer(&mut reader, context, call_site_encoding);
+ let cs_lpad = read_encoded_pointer(&mut reader, context, call_site_encoding);
+ let cs_action = reader.read_uleb128();
+ // Callsite table is sorted by cs_start, so if we've passed the ip, we
+ // may stop searching.
+ if ip < func_start + cs_start {
+ break;
+ }
+ if ip < func_start + cs_start + cs_len {
+ if cs_lpad == 0 {
+ return EHAction::None;
+ } else {
+ let lpad = lpad_base + cs_lpad;
+ return interpret_cs_action(cs_action, lpad);
+ }
+ }
}
- if ip < func_start + cs_start + cs_len {
- if cs_lpad != 0 {
- return Some(lpad_base + cs_lpad);
- } else {
- return None;
+ // If ip is not present in the table, call terminate. This is for
+ // a destructor inside a cleanup, or a library routine the compiler
+ // was not expecting to throw
+ EHAction::Terminate
+ } else {
+ // SjLj version:
+ // The "IP" is an index into the call-site table, with two exceptions:
+ // -1 means 'no-action', and 0 means 'terminate'.
+ match ip as isize {
+ -1 => return EHAction::None,
+ 0 => return EHAction::Terminate,
+ _ => (),
+ }
+ let mut idx = ip;
+ loop {
+ let cs_lpad = reader.read_uleb128();
+ let cs_action = reader.read_uleb128();
+ idx -= 1;
+ if idx == 0 {
+ // Can never have null landing pad for sjlj -- that would have
+ // been indicated by a -1 call site index.
+ let lpad = (cs_lpad + 1) as usize;
+ return interpret_cs_action(cs_action, lpad);
}
}
}
- // IP range not found: gcc's C++ personality calls terminate() here,
- // however the rest of the languages treat this the same as cs_lpad == 0.
- // We follow this suit.
- None
+}
+
+fn interpret_cs_action(cs_action: u64, lpad: usize) -> EHAction {
+ if cs_action == 0 {
+ EHAction::Cleanup(lpad)
+ } else {
+ EHAction::Catch(lpad)
+ }
}
#[inline]
DW_EH_PE_absptr => 0,
// relative to address of the encoded value, despite the name
DW_EH_PE_pcrel => reader.ptr as usize,
- DW_EH_PE_textrel => {
- assert!(context.text_start != 0);
- context.text_start
- }
- DW_EH_PE_datarel => {
- assert!(context.data_start != 0);
- context.data_start
- }
DW_EH_PE_funcrel => {
assert!(context.func_start != 0);
context.func_start
}
+ DW_EH_PE_textrel => {
+ (*context.get_text_start)()
+ }
+ DW_EH_PE_datarel => {
+ (*context.get_data_start)()
+ }
_ => panic!(),
};
0x4d4f5a_00_52555354
}
-// We could implement our personality routine in Rust, however exception
-// info decoding is tedious. More importantly, personality routines have to
-// handle various platform quirks, which are not fun to maintain. For this
-// reason, we attempt to reuse personality routine of the C language:
-// __gcc_personality_v0.
-//
-// Since C does not support exception catching, __gcc_personality_v0 simply
-// always returns _URC_CONTINUE_UNWIND in search phase, and always returns
-// _URC_INSTALL_CONTEXT (i.e. "invoke cleanup code") in cleanup phase.
-//
-// This is pretty close to Rust's exception handling approach, except that Rust
-// does have a single "catch-all" handler at the bottom of each thread's stack.
-// So we have two versions of the personality routine:
-// - rust_eh_personality, used by all cleanup landing pads, which never catches,
-// so the behavior of __gcc_personality_v0 is perfectly adequate there, and
-// - rust_eh_personality_catch, used only by rust_try(), which always catches.
-//
-// See also: rustc_trans::trans::intrinsic::trans_gnu_try
-
-#[cfg(all(not(target_arch = "arm"),
- not(all(windows, target_arch = "x86_64"))))]
+// All targets, except ARM which uses a slightly different ABI (however, iOS goes here as it uses
+// SjLj unwinding). Also, 64-bit Windows implementation lives in seh64_gnu.rs
+#[cfg(all(any(target_os = "ios", not(target_arch = "arm"))))]
pub mod eabi {
use unwind as uw;
- use libc::c_int;
+ use libc::{c_int, uintptr_t};
+ use dwarf::eh::{EHContext, EHAction, find_eh_action};
- extern "C" {
- fn __gcc_personality_v0(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code;
- }
+ // Register ids were lifted from LLVM's TargetLowering::getExceptionPointerRegister()
+ // and TargetLowering::getExceptionSelectorRegister() for each architecture,
+ // then mapped to DWARF register numbers via register definition tables
+ // (typically <arch>RegisterInfo.td, search for "DwarfRegNum").
+ // See also http://llvm.org/docs/WritingAnLLVMBackend.html#defining-a-register.
- #[lang = "eh_personality"]
- #[no_mangle]
- extern "C" fn rust_eh_personality(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code {
- unsafe { __gcc_personality_v0(version, actions, exception_class, ue_header, context) }
- }
+ #[cfg(target_arch = "x86")]
+ const UNWIND_DATA_REG: (i32, i32) = (0, 2); // EAX, EDX
- #[lang = "eh_personality_catch"]
- #[no_mangle]
- pub extern "C" fn rust_eh_personality_catch(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code {
+ #[cfg(target_arch = "x86_64")]
+ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // RAX, RDX
- if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 {
- // search phase
- uw::_URC_HANDLER_FOUND // catch!
- } else {
- // cleanup phase
- unsafe { __gcc_personality_v0(version, actions, exception_class, ue_header, context) }
- }
- }
-}
-
-// iOS on armv7 is using SjLj exceptions and therefore requires to use
-// a specialized personality routine: __gcc_personality_sj0
+ #[cfg(any(target_arch = "arm", target_arch = "aarch64"))]
+ const UNWIND_DATA_REG: (i32, i32) = (0, 1); // R0, R1 / X0, X1
-#[cfg(all(target_os = "ios", target_arch = "arm"))]
-pub mod eabi {
- use unwind as uw;
- use libc::c_int;
+ #[cfg(any(target_arch = "mips", target_arch = "mipsel"))]
+ const UNWIND_DATA_REG: (i32, i32) = (4, 5); // A0, A1
- extern "C" {
- fn __gcc_personality_sj0(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code;
- }
+ #[cfg(any(target_arch = "powerpc", target_arch = "powerpc64"))]
+ const UNWIND_DATA_REG: (i32, i32) = (3, 4); // R3, R4 / X3, X4
+ // Based on GCC's C and C++ personality routines. For reference, see:
+ // https://github.com/gcc-mirror/gcc/blob/master/libstdc++-v3/libsupc++/eh_personality.cc
+ // https://github.com/gcc-mirror/gcc/blob/trunk/libgcc/unwind-c.c
#[lang = "eh_personality"]
#[no_mangle]
- pub extern "C" fn rust_eh_personality(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code {
- unsafe { __gcc_personality_sj0(version, actions, exception_class, ue_header, context) }
+ #[allow(unused)]
+ unsafe extern "C" fn rust_eh_personality(version: c_int,
+ actions: uw::_Unwind_Action,
+ exception_class: uw::_Unwind_Exception_Class,
+ exception_object: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context)
+ -> uw::_Unwind_Reason_Code {
+ if version != 1 {
+ return uw::_URC_FATAL_PHASE1_ERROR;
+ }
+ let lsda = uw::_Unwind_GetLanguageSpecificData(context) as *const u8;
+ let mut ip_before_instr: c_int = 0;
+ let ip = uw::_Unwind_GetIPInfo(context, &mut ip_before_instr);
+ let eh_context = EHContext {
+ // The return address points 1 byte past the call instruction,
+ // which could be in the next IP range in LSDA range table.
+ ip: if ip_before_instr != 0 { ip } else { ip - 1 },
+ func_start: uw::_Unwind_GetRegionStart(context),
+ get_text_start: &|| uw::_Unwind_GetTextRelBase(context),
+ get_data_start: &|| uw::_Unwind_GetDataRelBase(context),
+ };
+ let eh_action = find_eh_action(lsda, &eh_context);
+
+ if actions as i32 & uw::_UA_SEARCH_PHASE as i32 != 0 {
+ match eh_action {
+ EHAction::None | EHAction::Cleanup(_) => return uw::_URC_CONTINUE_UNWIND,
+ EHAction::Catch(_) => return uw::_URC_HANDLER_FOUND,
+ EHAction::Terminate => return uw::_URC_FATAL_PHASE1_ERROR,
+ }
+ } else {
+ match eh_action {
+ EHAction::None => return uw::_URC_CONTINUE_UNWIND,
+ EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => {
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.0, exception_object as uintptr_t);
+ uw::_Unwind_SetGR(context, UNWIND_DATA_REG.1, 0);
+ uw::_Unwind_SetIP(context, lpad);
+ return uw::_URC_INSTALL_CONTEXT;
+ }
+ EHAction::Terminate => return uw::_URC_FATAL_PHASE2_ERROR,
+ }
+ }
}
+ #[cfg(stage0)]
#[lang = "eh_personality_catch"]
#[no_mangle]
- pub extern "C" fn rust_eh_personality_catch(version: c_int,
- actions: uw::_Unwind_Action,
- exception_class: uw::_Unwind_Exception_Class,
- ue_header: *mut uw::_Unwind_Exception,
- context: *mut uw::_Unwind_Context)
- -> uw::_Unwind_Reason_Code {
- if (actions as c_int & uw::_UA_SEARCH_PHASE as c_int) != 0 {
- // search phase
- uw::_URC_HANDLER_FOUND // catch!
- } else {
- // cleanup phase
- unsafe { __gcc_personality_sj0(version, actions, exception_class, ue_header, context) }
- }
+ pub unsafe extern "C" fn rust_eh_personality_catch(version: c_int,
+ actions: uw::_Unwind_Action,
+ exception_class: uw::_Unwind_Exception_Class,
+ ue_header: *mut uw::_Unwind_Exception,
+ context: *mut uw::_Unwind_Context)
+ -> uw::_Unwind_Reason_Code {
+ rust_eh_personality(version, actions, exception_class, ue_header, context)
}
}
-
// ARM EHABI uses a slightly different personality routine signature,
// but otherwise works the same.
#[cfg(all(target_arch = "arm", not(target_os = "ios")))]
// Entry point for raising an exception, just delegates to the platform-specific
// implementation.
#[no_mangle]
+#[unwind]
pub unsafe extern "C" fn __rust_start_panic(data: usize, vtable: usize) -> u32 {
imp::panic(mem::transmute(raw::TraitObject {
data: data as *mut (),
use core::any::Any;
use core::intrinsics;
use core::ptr;
-use dwarf::eh;
+use dwarf::eh::{EHContext, EHAction, find_eh_action};
use windows as c;
// Define our exception codes:
// This is considered acceptable, because the behavior of throwing exceptions
// through a C ABI boundary is undefined.
+#[cfg(stage0)]
#[lang = "eh_personality_catch"]
#[cfg(not(test))]
unsafe extern "C" fn rust_eh_personality_catch(exceptionRecord: *mut c::EXCEPTION_RECORD,
}
unsafe fn find_landing_pad(dc: &c::DISPATCHER_CONTEXT) -> Option<usize> {
- let eh_ctx = eh::EHContext {
- ip: dc.ControlPc as usize,
+ let eh_ctx = EHContext {
+ // The return address points 1 byte past the call instruction,
+ // which could be in the next IP range in LSDA range table.
+ ip: dc.ControlPc as usize - 1,
func_start: dc.ImageBase as usize + (*dc.FunctionEntry).BeginAddress as usize,
- text_start: dc.ImageBase as usize,
- data_start: 0,
+ get_text_start: &|| dc.ImageBase as usize,
+ get_data_start: &|| unimplemented!(),
};
- eh::find_landing_pad(dc.HandlerData, &eh_ctx)
+ match find_eh_action(dc.HandlerData, &eh_ctx) {
+ EHAction::None => None,
+ EHAction::Cleanup(lpad) | EHAction::Catch(lpad) => Some(lpad),
+ EHAction::Terminate => intrinsics::abort(),
+ }
}
"set the MIR optimization level (0-3)"),
dump_mir: Option<String> = (None, parse_opt_string,
"dump MIR state at various points in translation"),
+ dump_mir_dir: Option<String> = (None, parse_opt_string,
+ "the directory the MIR is dumped into"),
orbit: bool = (false, parse_bool,
"get MIR where it belongs - everywhere; most importantly, in orbit"),
}
target_vendor: "unknown".to_string(),
options: TargetOptions {
cpu: "mips32r2".to_string(),
- features: "+mips32r2,+soft-float".to_string(),
+ features: "+mips32r2".to_string(),
max_atomic_width: 32,
..super::linux_base::opts()
},
Float(f) => cast_const_float(tcx, f, ty),
Char(c) => cast_const_int(tcx, Infer(c as u64), ty),
Function(_) => Err(UnimplementedConstVal("casting fn pointers")),
- ByteStr(_) => match ty.sty {
+ ByteStr(b) => match ty.sty {
ty::TyRawPtr(_) => {
Err(ErrKind::UnimplementedConstVal("casting a bytestr to a raw ptr"))
},
- ty::TyRef(..) => Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice")),
+ ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
+ ty::TyArray(ty, n) if ty == tcx.types.u8 && n == b.len() => Ok(ByteStr(b)),
+ ty::TySlice(_) => {
+ Err(ErrKind::UnimplementedConstVal("casting a bytestr to slice"))
+ },
+ _ => Err(CannotCast),
+ },
+ _ => Err(CannotCast),
+ },
+ Str(s) => match ty.sty {
+ ty::TyRawPtr(_) => Err(ErrKind::UnimplementedConstVal("casting a str to a raw ptr")),
+ ty::TyRef(_, ty::TypeAndMut { ref ty, mutbl: hir::MutImmutable }) => match ty.sty {
+ ty::TyStr => Ok(Str(s)),
+ _ => Err(CannotCast),
+ },
_ => Err(CannotCast),
},
_ => Err(CannotCast),
use std::fs;
use std::io::{self, Write};
use syntax::ast::NodeId;
+use std::path::{PathBuf, Path};
const INDENT: &'static str = " ";
/// Alignment for lining up comments following MIR statements
_ => String::new()
};
+ let mut file_path = PathBuf::new();
+ if let Some(ref file_dir) = tcx.sess.opts.debugging_opts.dump_mir_dir {
+ let p = Path::new(file_dir);
+ file_path.push(p);
+ };
let file_name = format!("rustc.node{}{}.{}.{}.mir",
node_id, promotion_id, pass_name, disambiguator);
- let _ = fs::File::create(&file_name).and_then(|mut file| {
+ file_path.push(&file_name);
+ let _ = fs::File::create(&file_path).and_then(|mut file| {
try!(writeln!(file, "// MIR for `{}`", node_path));
try!(writeln!(file, "// node_id = {}", node_id));
try!(writeln!(file, "// pass_name = {}", pass_name));
use Disr;
use util::common::indenter;
use util::sha2::Sha256;
-use util::nodemap::{NodeMap, NodeSet};
+use util::nodemap::{NodeMap, NodeSet, FnvHashSet};
use arena::TypedArena;
use libc::c_uint;
use std::ffi::{CStr, CString};
+use std::borrow::Cow;
use std::cell::{Cell, RefCell};
-use std::collections::{HashMap, HashSet};
+use std::collections::HashMap;
use std::ptr;
use std::rc::Rc;
use std::str;
/// Find any symbols that are defined in one compilation unit, but not declared
/// in any other compilation unit. Give these symbols internal linkage.
-fn internalize_symbols(cx: &CrateContextList, reachable: &HashSet<&str>) {
+fn internalize_symbols<'a, 'tcx>(ccxs: &CrateContextList<'a, 'tcx>,
+ symbol_map: &SymbolMap<'tcx>,
+ reachable: &FnvHashSet<&str>) {
+ let scx = ccxs.shared();
+ let tcx = scx.tcx();
+
+ // 'unsafe' because we are holding on to CStr's from the LLVM module within
+ // this block.
unsafe {
- let mut declared = HashSet::new();
+ let mut referenced_somewhere = FnvHashSet();
- // Collect all external declarations in all compilation units.
- for ccx in cx.iter() {
+ // Collect all symbols that need to stay externally visible because they
+ // are referenced via a declaration in some other codegen unit.
+ for ccx in ccxs.iter() {
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
let linkage = llvm::LLVMGetLinkage(val);
// We only care about external declarations (not definitions)
let is_decl = llvm::LLVMIsDeclaration(val) != 0;
if is_decl || is_available_externally {
- let name = CStr::from_ptr(llvm::LLVMGetValueName(val));
- declared.insert(name);
+ let symbol_name = CStr::from_ptr(llvm::LLVMGetValueName(val));
+ referenced_somewhere.insert(symbol_name);
}
}
}
+ // Also collect all symbols for which we cannot adjust linkage, because
+ // it is fixed by some directive in the source code (e.g. #[no_mangle]).
+ let linkage_fixed_explicitly: FnvHashSet<_> = scx
+ .translation_items()
+ .borrow()
+ .iter()
+ .cloned()
+ .filter(|trans_item|{
+ let def_id = match *trans_item {
+ TransItem::DropGlue(..) => {
+ return false
+ },
+ TransItem::Fn(ref instance) => {
+ instance.def
+ }
+ TransItem::Static(node_id) => {
+ tcx.map.local_def_id(node_id)
+ }
+ };
+
+ trans_item.explicit_linkage(tcx).is_some() ||
+ attr::contains_extern_indicator(tcx.sess.diagnostic(),
+ &tcx.get_attrs(def_id))
+ })
+ .map(|trans_item| symbol_map.get_or_compute(scx, trans_item))
+ .collect();
+
// Examine each external definition. If the definition is not used in
// any other compilation unit, and is not reachable from other crates,
// then give it internal linkage.
- for ccx in cx.iter() {
+ for ccx in ccxs.iter() {
for val in iter_globals(ccx.llmod()).chain(iter_functions(ccx.llmod())) {
let linkage = llvm::LLVMGetLinkage(val);
let is_externally_visible = (linkage == llvm::ExternalLinkage as c_uint) ||
(linkage == llvm::LinkOnceODRLinkage as c_uint) ||
(linkage == llvm::WeakODRLinkage as c_uint);
- let is_definition = llvm::LLVMIsDeclaration(val) != 0;
+ let is_definition = llvm::LLVMIsDeclaration(val) == 0;
// If this is a definition (as opposed to just a declaration)
// and externally visible, check if we can internalize it
if is_definition && is_externally_visible {
let name_cstr = CStr::from_ptr(llvm::LLVMGetValueName(val));
let name_str = name_cstr.to_str().unwrap();
+ let name_cow = Cow::Borrowed(name_str);
- let is_referenced_somewhere = declared.contains(&name_cstr);
- let is_reachable = reachable.contains(name_str);
+ let is_referenced_somewhere = referenced_somewhere.contains(&name_cstr);
+ let is_reachable = reachable.contains(&name_str);
+ let has_fixed_linkage = linkage_fixed_explicitly.contains(&name_cow);
- if !is_referenced_somewhere && !is_reachable {
+ if !is_referenced_somewhere && !is_reachable && !has_fixed_linkage {
llvm::SetLinkage(val, llvm::InternalLinkage);
llvm::SetDLLStorageClass(val, llvm::DefaultStorageClass);
llvm::UnsetComdat(val);
}
-
}
}
}
}));
}
- internalize_symbols(&crate_context_list,
- &reachable_symbols.iter().map(|x| &x[..]).collect());
+ time(shared_ccx.sess().time_passes(), "internalize symbols", || {
+ internalize_symbols(&crate_context_list,
+ &symbol_map,
+ &reachable_symbols.iter()
+ .map(|s| &s[..])
+ .collect())
+ });
if sess.target.target.options.is_like_msvc &&
sess.crate_types.borrow().iter().any(|ct| *ct == config::CrateTypeRlib) {
// managed by the standard library.
attributes::emit_uwtable(bcx.fcx.llfn, true);
- let catch_pers = match tcx.lang_items.eh_personality_catch() {
- Some(did) => {
- Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val
+ let target = &bcx.sess().target.target;
+ let catch_pers = if target.arch == "arm" && target.target_os != "ios" {
+ // Only ARM still uses a separate catch personality (for now)
+ match tcx.lang_items.eh_personality_catch() {
+ Some(did) => {
+ Callee::def(ccx, did, tcx.mk_substs(Substs::empty())).reify(ccx).val
+ }
+ None => bug!("eh_personality_catch not defined"),
}
- None => bug!("eh_personality_catch not defined"),
+ } else {
+ bcx.fcx.eh_personality()
};
let then = bcx.fcx.new_temp_block("then");
token::Dot | token::DotDot | token::DotDotDot | token::Comma | token::Semi |
token::Colon | token::ModSep | token::LArrow | token::OpenDelim(_) |
token::CloseDelim(token::Brace) | token::CloseDelim(token::Paren) |
+ token::CloseDelim(token::NoDelim) |
token::Question => Class::None,
token::Dollar => {
if self.lexer.peek().tok.is_ident() {
margin-right: 5px;
}
-.enum > .toggle-wrapper > .collapse-toggle, .struct > .toggle-wrapper > .collapse-toggle {
+.toggle-wrapper > .collapse-toggle {
left: 0;
+}
+
+.variant + .toggle-wrapper > a {
margin-top: 5px;
}
),
}
+#[stable(feature= "debug_hash_map", since = "1.12.0")]
+impl<'a, K: 'a + Debug, 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 single occupied location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct OccupiedEntry<'a, K: 'a, V: 'a> {
elem: FullBucket<K, V, &'a mut RawTable<K, V>>,
}
+#[stable(feature= "debug_hash_map", since = "1.12.0")]
+impl<'a, K: 'a + Debug, 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()
+ }
+}
+
/// A view into a single empty location in a HashMap.
#[stable(feature = "rust1", since = "1.0.0")]
pub struct VacantEntry<'a, K: 'a, V: 'a> {
elem: VacantEntryState<K, V, &'a mut RawTable<K, V>>,
}
+#[stable(feature= "debug_hash_map", since = "1.12.0")]
+impl<'a, K: 'a + Debug, 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()
+ }
+}
+
/// Possible states of a VacantEntry.
enum VacantEntryState<K, V, M> {
/// The index is occupied, but the key to insert has precedence,
/// A particular instance `RandomState` will create the same instances of
/// `Hasher`, but the hashers created by two different `RandomState`
/// instances are unlikely to produce the same result for the same values.
+///
+/// # Examples
+///
+/// ```
+/// use std::collections::HashMap;
+/// use std::collections::hash_map::RandomState;
+///
+/// let s = RandomState::new();
+/// let mut map = HashMap::with_hasher(s);
+/// map.insert(1, 2);
+/// ```
#[derive(Clone)]
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
pub struct RandomState {
impl RandomState {
/// Constructs a new `RandomState` that is initialized with random keys.
+ ///
+ /// # Examples
+ ///
+ /// ```
+ /// use std::collections::hash_map::RandomState;
+ ///
+ /// let s = RandomState::new();
+ /// ```
#[inline]
#[allow(deprecated)] // rand
#[stable(feature = "hashmap_build_hasher", since = "1.7.0")]
impl DirEntry {
/// Returns the full path to the file that this entry represents.
///
- /// The full path is created by joining the original path to `read_dir` or
- /// `walk_dir` with the filename of this entry.
+ /// The full path is created by joining the original path to `read_dir`
+ /// with the filename of this entry.
///
/// # Examples
///
rx: &'a Receiver<T>
}
+/// An iterator that attempts to yield all pending values for a receiver.
+/// `None` will be returned when there are no pending values remaining or
+/// if the corresponding channel has hung up.
+///
+/// This Iterator will never block the caller in order to wait for data to
+/// become available. Instead, it will return `None`.
+#[unstable(feature = "receiver_try_iter", issue = "34931")]
+pub struct TryIter<'a, T: 'a> {
+ rx: &'a Receiver<T>
+}
+
/// An owning iterator over messages on a receiver, this iterator will block
/// whenever `next` is called, waiting for a new message, and `None` will be
/// returned when the corresponding channel has hung up.
pub fn iter(&self) -> Iter<T> {
Iter { rx: self }
}
+
+ /// Returns an iterator that will attempt to yield all pending values.
+ /// It will return `None` if there are no more pending values or if the
+ /// channel has hung up. The iterator will never `panic!` or block the
+ /// user by waiting for values.
+ #[unstable(feature = "receiver_try_iter", issue = "34931")]
+ pub fn try_iter(&self) -> TryIter<T> {
+ TryIter { rx: self }
+ }
+
}
impl<T> select::Packet for Receiver<T> {
fn next(&mut self) -> Option<T> { self.rx.recv().ok() }
}
+#[unstable(feature = "receiver_try_iter", issue = "34931")]
+impl<'a, T> Iterator for TryIter<'a, T> {
+ type Item = T;
+
+ fn next(&mut self) -> Option<T> { self.rx.try_recv().ok() }
+}
+
#[stable(feature = "receiver_into_iter", since = "1.1.0")]
impl<'a, T> IntoIterator for &'a Receiver<T> {
type Item = T;
assert_eq!(count_rx.recv().unwrap(), 4);
}
+ #[test]
+ fn test_recv_try_iter() {
+ let (request_tx, request_rx) = channel();
+ let (response_tx, response_rx) = channel();
+
+ // Request `x`s until we have `6`.
+ let t = thread::spawn(move|| {
+ let mut count = 0;
+ loop {
+ for x in response_rx.try_iter() {
+ count += x;
+ if count == 6 {
+ return count;
+ }
+ }
+ request_tx.send(()).unwrap();
+ }
+ });
+
+ for _ in request_rx.iter() {
+ if response_tx.send(2).is_err() {
+ break;
+ }
+ }
+
+ assert_eq!(t.join().unwrap(), 6);
+ }
+
#[test]
fn test_recv_into_iter_owned() {
let mut iter = {
fn mk_delim(cx: &ExtCtxt, sp: Span, delim: token::DelimToken) -> P<ast::Expr> {
let name = match delim {
- token::Paren => "Paren",
- token::Bracket => "Bracket",
- token::Brace => "Brace",
+ token::Paren => "Paren",
+ token::Bracket => "Bracket",
+ token::Brace => "Brace",
+ token::NoDelim => "NoDelim",
};
mk_token_path(cx, sp, name)
}
pub tokens_consumed: usize,
pub restrictions: Restrictions,
pub quote_depth: usize, // not (yet) related to the quasiquoter
+ parsing_token_tree: bool,
pub reader: Box<Reader+'a>,
/// The set of seen errors about obsolete syntax. Used to suppress
/// extra detail when the same error is seen twice
tokens_consumed: 0,
restrictions: Restrictions::empty(),
quote_depth: 0,
+ parsing_token_tree: false,
obsolete_set: HashSet::new(),
mod_path_stack: Vec::new(),
filename: filename,
}
pub fn check_unknown_macro_variable(&mut self) {
- if self.quote_depth == 0 {
+ if self.quote_depth == 0 && !self.parsing_token_tree {
match self.token {
token::SubstNt(name) =>
self.fatal(&format!("unknown macro variable `{}`", name)).emit(),
Err(err)
},
token::OpenDelim(delim) => {
+ let parsing_token_tree = ::std::mem::replace(&mut self.parsing_token_tree, true);
// The span for beginning of the delimited section
let pre_span = self.span;
_ => {}
}
+ self.parsing_token_tree = parsing_token_tree;
Ok(TokenTree::Delimited(span, Rc::new(Delimited {
delim: delim,
open_span: open_span,
Bracket,
/// A curly brace: `{` or `}`
Brace,
+ /// An empty delimiter
+ NoDelim,
}
#[derive(Clone, RustcEncodable, RustcDecodable, PartialEq, Eq, Hash, Debug, Copy)]
token::CloseDelim(token::Bracket) => "]".to_string(),
token::OpenDelim(token::Brace) => "{".to_string(),
token::CloseDelim(token::Brace) => "}".to_string(),
+ token::OpenDelim(token::NoDelim) => " ".to_string(),
+ token::CloseDelim(token::NoDelim) => " ".to_string(),
token::Pound => "#".to_string(),
token::Dollar => "$".to_string(),
token::Question => "?".to_string(),
try!(self.head(""));
try!(self.bopen());
}
+ token::NoDelim => {}
}
try!(self.print_tts(&m.node.tts));
match delim {
token::Paren => self.pclose(),
token::Bracket => word(&mut self.s, "]"),
token::Brace => self.bclose(m.span),
+ token::NoDelim => Ok(()),
}
}
}
}
- pub fn from_span(primary_span: Span) -> MultiSpan {
- MultiSpan {
- primary_spans: vec![primary_span],
- span_labels: vec![]
- }
- }
-
- pub fn from_spans(vec: Vec<Span>) -> MultiSpan {
- MultiSpan {
- primary_spans: vec,
- span_labels: vec![]
- }
- }
-
pub fn push_span_label(&mut self, span: Span, label: String) {
self.span_labels.push((span, label));
}
impl From<Span> for MultiSpan {
fn from(span: Span) -> MultiSpan {
- MultiSpan::from_span(span)
+ MultiSpan {
+ primary_spans: vec![span],
+ span_labels: vec![]
+ }
}
}
pub type _Unwind_Exception_Class = u64;
pub type _Unwind_Word = libc::uintptr_t;
+pub type _Unwind_Ptr = libc::uintptr_t;
pub type _Unwind_Trace_Fn = extern "C" fn(ctx: *mut _Unwind_Context, arg: *mut libc::c_void)
-> _Unwind_Reason_Code;
ip_before_insn: *mut libc::c_int)
-> libc::uintptr_t;
+ pub fn _Unwind_GetLanguageSpecificData(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
+ pub fn _Unwind_GetRegionStart(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
+ pub fn _Unwind_GetTextRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
+ pub fn _Unwind_GetDataRelBase(ctx: *mut _Unwind_Context) -> _Unwind_Ptr;
+ pub fn _Unwind_SetGR(ctx: *mut _Unwind_Context, reg_index: libc::c_int, value: _Unwind_Ptr);
+ pub fn _Unwind_SetIP(ctx: *mut _Unwind_Context, value: _Unwind_Ptr);
+
#[cfg(all(not(target_os = "android"),
not(all(target_os = "linux", target_arch = "arm"))))]
pub fn _Unwind_FindEnclosingFunction(pc: *mut libc::c_void) -> *mut libc::c_void;
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// compile-flags: -C no-prepopulate-passes
+
+pub fn main() {
+
+ // We want to make sure that closures get 'internal' linkage instead of
+ // 'weak_odr' when they are not shared between codegen units
+ // CHECK: define internal {{.*}}_ZN20internalize_closures4main{{.*}}$u7b$$u7b$closure$u7d$$u7d$
+ let c = |x:i32| { x + 1 };
+ let _ = c(1);
+}
--- /dev/null
+This folder contains tests for MIR optimizations.
+
+The test format is:
+
+```
+(arbitrary rust code)
+// END RUST SOURCE
+// START $file_name_of_some_mir_dump_0
+// $expected_line_0
+// ...
+// $expected_line_N
+// END $file_name_of_some_mir_dump_0
+// ...
+// START $file_name_of_some_mir_dump_N
+// $expected_line_0
+// ...
+// $expected_line_N
+// END $file_name_of_some_mir_dump_N
+```
+
+All the test information is in comments so the test is runnable.
+
+For each $file_name, compiletest expects [$expected_line_0, ...,
+$expected_line_N] to appear in the dumped MIR in order. Currently it allows
+other non-matched lines before, after and in-between.
+
+Lines match ignoring whitespace, and the prefix "//" is removed.
+
+It also currently strips trailing comments -- partly because the full file path
+in "scope comments" is unpredictable and partly because tidy complains about
+the lines being too long.
+
+compiletest handles dumping the MIR before and after every pass for you. The
+test writer only has to specify the file names of the dumped files (not the
+full path to the file) and what lines to expect. I added an option to rustc
+that tells it to dump the mir into some directly (rather then always dumping to
+the current directory).
+
+Lines match ignoring whitespace, and the prefix "//" is removed of course.
+
+It also currently strips trailing comments -- partly because the full file path
+in "scope comments" is unpredictable and partly because tidy complains about
+the lines being too long.
+
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+// this tests move up progration, which is not yet implemented
+
+fn foo() -> [u8; 1024] {
+ let x = [0; 1024];
+ return x;
+}
+
+fn main() { }
\ No newline at end of file
--- /dev/null
+// 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 <LICENSE-APACHE or
+// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
+// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
+// option. This file may not be copied, modified, or distributed
+// except according to those terms.
+
+fn main() {
+ if false {
+ println!("hello world!");
+ }
+}
+
+// END RUST SOURCE
+// START rustc.node4.SimplifyBranches.initial-before.mir
+// bb0: {
+// if(const false) -> [true: bb1, false: bb2]; // scope 0 at simplify_if.rs:12:5: 14:6
+// }
+// END rustc.node4.SimplifyBranches.initial-before.mir
+// START rustc.node4.SimplifyBranches.initial-after.mir
+// bb0: {
+// goto -> bb2; // scope 0 at simplify_if.rs:12:5: 14:6
+// }
+// END rustc.node4.SimplifyBranches.initial-after.mir
\ No newline at end of file
-// Copyright 2013 The Rust Project Developers. See the COPYRIGHT
+// 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.
//
pub fn main() {
let _ = b"x" as &[u8];
+ let _ = b"y" as &[u8; 1];
+ let _ = b"z" as *const u8;
+ let _ = "รค" as *const str;
}
t!(format!("{:<4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(format!("{:>4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
t!(format!("{:^4.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
- t!(format!("{:>10.4}", "aaaaaaaaaaaaaaaaaa"), "aaaa");
+ t!(format!("{:>10.4}", "aaaaaaaaaaaaaaaaaa"), " aaaa");
t!(format!("{:2.4}", "aaaaa"), "aaaa");
t!(format!("{:2.4}", "aaaa"), "aaaa");
t!(format!("{:2.4}", "aaa"), "aaa");
t!(format!("{:a$}", "a", a=4), "a ");
t!(format!("{:-#}", "a"), "a");
t!(format!("{:+#}", "a"), "a");
+ t!(format!("{:/^10.8}", "1234567890"), "/12345678/");
// Some float stuff
t!(format!("{:}", 1.0f32), "1");
});
}
+macro_rules! outer {
+ ($x:expr; $fragment:ident) => {
+ macro_rules! inner { ($y:$fragment) => { $x + $y } }
+ }
+}
+
fn main() {
let val = higher_order!(subst ($x:expr, $y:expr, $foo:expr) => (($x + $y, $foo)));
assert_eq!(val, (3, "foo"));
+
+ outer!(2; expr);
+ assert_eq!(inner!(3), 5);
}
Incremental,
RunMake,
Ui,
+ MirOpt,
}
impl FromStr for Mode {
"incremental" => Ok(Incremental),
"run-make" => Ok(RunMake),
"ui" => Ok(Ui),
+ "mir-opt" => Ok(MirOpt),
_ => Err(()),
}
}
Incremental => "incremental",
RunMake => "run-make",
Ui => "ui",
+ MirOpt => "mir-opt",
}, f)
}
}
reqopt("", "stage-id", "the target-stage identifier", "stageN-TARGET"),
reqopt("", "mode", "which sort of compile tests to run",
"(compile-fail|parse-fail|run-fail|run-pass|\
- run-pass-valgrind|pretty|debug-info|incremental)"),
+ run-pass-valgrind|pretty|debug-info|incremental|mir-opt)"),
optflag("", "ignored", "run tests marked as ignored"),
optopt("", "runtool", "supervisor program to run tests under \
(eg. emulator, valgrind)", "PROGRAM"),
use common::Config;
use common::{CompileFail, ParseFail, Pretty, RunFail, RunPass, RunPassValgrind};
use common::{Codegen, DebugInfoLldb, DebugInfoGdb, Rustdoc, CodegenUnits};
-use common::{Incremental, RunMake, Ui};
+use common::{Incremental, RunMake, Ui, MirOpt};
use errors::{self, ErrorKind, Error};
use json;
use header::TestProps;
Incremental => self.run_incremental_test(),
RunMake => self.run_rmake_test(),
Ui => self.run_ui_test(),
+ MirOpt => self.run_mir_opt_test(),
}
}
.map(|s| s.to_string()));
}
}
+ MirOpt => {
+ args.extend(["-Z",
+ "dump-mir=all",
+ "-Z"]
+ .iter()
+ .map(|s| s.to_string()));
+
+ let mir_dump_dir = self.get_mir_dump_dir();
+ self.create_dir_racy(mir_dump_dir.as_path());
+ let mut dir_opt = "dump-mir-dir=".to_string();
+ dir_opt.push_str(mir_dump_dir.to_str().unwrap());
+ debug!("dir_opt: {:?}", dir_opt);
+
+ args.push(dir_opt);
+ }
RunFail |
RunPass |
RunPassValgrind |
}
}
+ fn run_mir_opt_test(&self) {
+ let proc_res = self.compile_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("compilation failed!", &proc_res);
+ }
+
+ let proc_res = self.exec_compiled_test();
+
+ if !proc_res.status.success() {
+ self.fatal_proc_rec("test run failed!", &proc_res);
+ }
+ self.check_mir_dump();
+ }
+
+ fn check_mir_dump(&self) {
+ let mut test_file_contents = String::new();
+ fs::File::open(self.testpaths.file.clone()).unwrap()
+ .read_to_string(&mut test_file_contents)
+ .unwrap();
+ if let Some(idx) = test_file_contents.find("// END RUST SOURCE") {
+ let (_, tests_text) = test_file_contents.split_at(idx + "// END_RUST SOURCE".len());
+ let tests_text_str = String::from(tests_text);
+ let mut curr_test : Option<&str> = None;
+ let mut curr_test_contents = Vec::new();
+ for l in tests_text_str.lines() {
+ debug!("line: {:?}", l);
+ if l.starts_with("// START ") {
+ let (_, t) = l.split_at("// START ".len());
+ curr_test = Some(t);
+ } else if l.starts_with("// END") {
+ let (_, t) = l.split_at("// END ".len());
+ if Some(t) != curr_test {
+ panic!("mismatched START END test name");
+ }
+ self.compare_mir_test_output(curr_test.unwrap(), &curr_test_contents);
+ curr_test = None;
+ curr_test_contents.clear();
+ } else if l.is_empty() {
+ // ignore
+ } else if l.starts_with("// ") {
+ let (_, test_content) = l.split_at("// ".len());
+ curr_test_contents.push(test_content);
+ }
+ }
+ }
+ }
+
+ fn compare_mir_test_output(&self, test_name: &str, expected_content: &Vec<&str>) {
+ let mut output_file = PathBuf::new();
+ output_file.push(self.get_mir_dump_dir());
+ output_file.push(test_name);
+ debug!("comparing the contests of: {:?}", output_file);
+ debug!("with: {:?}", expected_content);
+
+ let mut dumped_file = fs::File::open(output_file.clone()).unwrap();
+ let mut dumped_string = String::new();
+ dumped_file.read_to_string(&mut dumped_string).unwrap();
+ let mut dumped_lines = dumped_string.lines().filter(|l| !l.is_empty());
+ let mut expected_lines = expected_content.iter().filter(|l| !l.is_empty());
+
+ // We expect each non-empty line from expected_content to appear
+ // in the dump in order, but there may be extra lines interleaved
+ while let Some(expected_line) = expected_lines.next() {
+ let e_norm = normalize_mir_line(expected_line);
+ if e_norm.is_empty() {
+ continue;
+ };
+ let mut found = false;
+ while let Some(dumped_line) = dumped_lines.next() {
+ let d_norm = normalize_mir_line(dumped_line);
+ debug!("found: {:?}", d_norm);
+ debug!("expected: {:?}", e_norm);
+ if e_norm == d_norm {
+ found = true;
+ break;
+ };
+ }
+ if !found {
+ panic!("ran out of mir dump output to match against");
+ }
+ }
+ }
+
+ fn get_mir_dump_dir(&self) -> PathBuf {
+ let mut mir_dump_dir = PathBuf::from(self.config.build_base
+ .as_path()
+ .to_str()
+ .unwrap());
+ debug!("input_file: {:?}", self.testpaths.file);
+ mir_dump_dir.push(self.testpaths.file.file_stem().unwrap().to_str().unwrap());
+ mir_dump_dir
+ }
+
fn normalize_output(&self, output: &str) -> String {
let parent_dir = self.testpaths.file.parent().unwrap();
let parent_dir_str = parent_dir.display().to_string();
ThisDirectory(PathBuf),
}
+fn normalize_mir_line(line: &str) -> String {
+ let no_comments = if let Some(idx) = line.find("//") {
+ let (l, _) = line.split_at(idx);
+ l
+ } else {
+ line
+ };
+ no_comments.replace(char::is_whitespace, "")
+}